1 /*
2 
3 Copyright (C) 2012-2019 Olaf Till <i7tiol@t-online.de>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 #include "pq_connection.h"
21 #include "command.h"
22 #include "error-helpers.h"
23 
24 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_pq_connection, "PGconn", "PGconn")
25 
pq_basetype_prefix(void)26 std::string &pq_basetype_prefix (void)
27 {
28   static std::string prefix ("pg_catalog.");
29   return prefix;
30 }
31 
32 const int pq_bpl = pq_basetype_prefix ().size ();
33 
map_str_cmp(const char * c1,const char * c2)34 static bool map_str_cmp (const char *c1, const char *c2)
35 {
36   if (strcmp (c1, c2) < 0)
37     return true;
38   else
39     return false;
40 }
41 
map_string_cmp(const std::string & s1,const std::string & s2)42 static bool map_string_cmp (const std::string &s1, const std::string &s2)
43 {
44   if (s1.compare (s2) < 0)
45     return true;
46   else
47     return false;
48 }
49 
octave_pq_connection_rep(std::string & arg)50 octave_pq_connection_rep::octave_pq_connection_rep (std::string &arg)
51 : conv_map (), name_conv_map (&map_str_cmp), conn (NULL)
52 {
53   BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
54   conn = PQconnectdb (arg.c_str ());
55   END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
56 
57   if (! conn || PQstatus (conn) == CONNECTION_BAD)
58     {
59       if (conn)
60         {
61           _p_error ("%s", PQerrorMessage (conn));
62 
63           PGconn *t_conn = conn;
64 
65           conn = NULL;
66 
67           BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
68           PQfinish (t_conn);
69           END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
70         }
71 
72       _p_error ("PQ connection attempt failed");
73     }
74   else
75     {
76       // init name converter-map (kind of "bootstrapping")
77       for (int i = 0; i < OCT_PQ_NUM_CONVERTERS; i++)
78         {
79           name_conv_map[conv_ptrs[i]->name.c_str ()] = conv_ptrs[i];
80 
81           // unqualified name, may be replaced later with user-defined type
82           name_conv_map[conv_ptrs[i]->name.c_str () + pq_bpl] = conv_ptrs[i];
83         }
84 
85       if (octave_pq_fill_base_types () ||
86           octave_pq_get_composite_types () ||
87           octave_pq_get_enum_types ())
88         {
89           PGconn *t_conn = conn;
90 
91           conn = NULL;
92 
93           BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
94           PQfinish (t_conn);
95           END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
96 
97           _p_error ("could not read types");
98         }
99       else
100         {
101           if (strcmp (PQparameterStatus (conn, "integer_datetimes"), "on"))
102             integer_datetimes = false;
103           else
104             integer_datetimes = true;
105         }
106     }
107 }
108 
~octave_pq_connection_rep(void)109 octave_pq_connection_rep::~octave_pq_connection_rep (void)
110 {
111   if (conn)
112     {
113       BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
114       PQfinish (conn);
115       END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
116 
117       octave_pq_delete_non_constant_types ();
118     }
119 }
120 
octave_pq_close(void)121 void octave_pq_connection_rep::octave_pq_close (void)
122 {
123   if (conn)
124     {
125       PGconn *t_conn = conn;
126 
127       conn = NULL;
128 
129       BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
130       PQfinish (t_conn);
131       END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
132 
133       octave_pq_delete_non_constant_types ();
134     }
135   else
136     // deliberately left the 'error' call here, since
137     // 'octave_pq_close()' is only called by 'pq_close' immediately
138     // before returning
139     error ("PGconn object not open");
140 }
141 
octave_pq_delete_non_constant_types(void)142 void octave_pq_connection_rep::octave_pq_delete_non_constant_types (void)
143 {
144   // In the first map, allocated types are usually referenced twice
145   // (by oid and aoid). Yet we need no refcount as long as we go
146   // through the name map as the last, since there (the same) types
147   // are only referenced once.
148 
149   std::vector<oct_pq_conv_map_t::iterator> t_it_v;
150 
151   for (oct_pq_conv_map_t::iterator it = conv_map.begin ();
152        it != conv_map.end ();
153        it++)
154     if (it->second->is_not_constant)
155       t_it_v.push_back (it);
156 
157   for (std::vector<oct_pq_conv_map_t::iterator>::iterator it = t_it_v.begin ();
158        it != t_it_v.end (); it++)
159     conv_map.erase (*it);
160 
161   std::vector<oct_pq_name_conv_map_t::iterator> t_name_it_v;
162 
163   for (oct_pq_name_conv_map_t::iterator it = name_conv_map.begin ();
164        it != name_conv_map.end ();
165        it++)
166     {
167       oct_pq_conv_t *conv = it->second;
168 
169       if (conv->is_not_constant)
170         {
171           t_name_it_v.push_back (it);
172 
173           delete conv;
174         }
175     }
176 
177   for (std::vector<oct_pq_name_conv_map_t::iterator>::iterator it =
178          t_name_it_v.begin ();
179        it != t_name_it_v.end (); it++)
180     name_conv_map.erase (*it);
181 }
182 
octave_pq_fill_base_types(void)183 int octave_pq_connection_rep::octave_pq_fill_base_types (void)
184 {
185   Cell p (0, 0), pt (0, 0), rt (3, 1);
186   rt(0) = octave_value ("oid");
187   rt(1) = octave_value ("name");
188   rt(2) = octave_value ("oid");
189 
190   std::string cmd ("select oid, typname, typarray from pg_type where (typtype = 'b' AND typarray != 0) OR typname = 'record' OR typname = 'unknown';"),
191     caller ("octave_pq_fill_base_types");
192 
193   command c (*this, cmd, p, pt, rt, caller);
194   if (! c.good ())
195     {
196       _p_error ("octave_pq_fill_base_types: could not read pg_type");
197       return 1;
198     }
199 
200   octave_value res = c.process_single_result ();
201   if (! c.good ())
202     return 1;
203 
204   Cell tpls;
205   bool err;
206   SET_ERR (tpls = res.scalar_map_value ().contents ("data").cell_value (),
207            err);
208   if (err)
209     {
210       _p_error
211         ("octave_pq_fill_base_types: could not convert result data to cell");
212       return 1;
213     }
214 
215   // make a temporary map of server base types (cell row numbers) for searching
216   typedef std::map<std::string, int, bool (*) (const std::string &,
217                                                const std::string &)>
218     bt_map_t;
219   bt_map_t bt_map (&map_string_cmp);
220   for (int i = 0; i < tpls.rows (); i++)
221     {
222       SET_ERR (bt_map[tpls(i, 1).string_value ()] = i, err);
223       if (err)
224         break;
225     }
226   if (err)
227     {
228       _p_error ("octave_pq_fill_base_types: could not read returned result");
229       return 1;
230     }
231 
232   for (int i = 0; i < OCT_PQ_NUM_CONVERTERS; i++)
233     {
234       bt_map_t::iterator bt_it;
235       if ((bt_it = bt_map.find (conv_ptrs[i]->name.c_str () + pq_bpl)) ==
236           bt_map.end ())
237         {
238           _p_error ("octave_pq_fill_base_types: type %s not found in pg_type",
239                     conv_ptrs[i]->name.c_str () + pq_bpl);
240           return 1;
241         }
242       // fill in oid and aoid into static records of converters
243       SET_ERR (conv_ptrs[i]->oid = tpls(bt_it->second, 0).int_value (), err);
244       if (! err)
245         {
246           SET_ERR (conv_ptrs[i]->aoid = tpls(bt_it->second, 2).int_value (),
247                    err);
248         }
249       if (err)
250         break;
251 
252       // fill in map of converters over oid with oid and, if not zero,
253       // also with aoid
254       conv_map[conv_ptrs[i]->oid] = conv_ptrs[i];
255       if (conv_ptrs[i]->aoid != 0)
256         conv_map[conv_ptrs[i]->aoid] = conv_ptrs[i];
257     }
258   if (err)
259     {
260       _p_error ("octave_pq_fill_base_types: could not read returned result");
261       return 1;
262     }
263 
264   return 0;
265 }
266 
octave_pq_get_composite_types(void)267 int octave_pq_connection_rep::octave_pq_get_composite_types (void)
268 {
269   Cell p, pt, rt;
270 
271   std::string cmd ("select pg_type.oid, pg_type.typname, pg_type.typarray, pg_namespace.nspname, pg_type_is_visible(pg_type.oid) as visible, array_agg(pg_attribute.atttypid), array_agg(pg_attribute.attnum) from (pg_type join pg_namespace on pg_type.typnamespace = pg_namespace.oid) join pg_attribute on pg_type.typrelid = pg_attribute.attrelid where pg_type.typtype = 'c' and pg_attribute.attnum > 0 group by pg_type.oid, pg_type.typname, pg_type.typarray, pg_type.typrelid, pg_namespace.nspname, visible;"),
272     caller ("octave_pq_get_composite_types");
273 
274   command c (*this, cmd, p, pt, rt, caller);
275   if (! c.good ())
276     {
277       _p_error ("octave_pq_get_composite_types: could not read pg_type");
278       return 1;
279     }
280 
281   octave_value res = c.process_single_result ();
282   if (! c.good ())
283     return 1;
284 
285   Cell tpls;
286   bool err;
287   SET_ERR (tpls = res.scalar_map_value ().contents ("data").cell_value (),
288            err)
289   if (err)
290     {
291       _p_error ("octave_pq_get_composite_types: could not convert result data to cell");
292       return 1;
293     }
294 
295   for (int i = 0; i < tpls.rows (); i++)
296     {
297       Oid oid;
298       SET_ERR (oid = tpls(i, 0).uint_value (), err);
299       Oid aoid;
300       if (! err)
301         {
302           SET_ERR (aoid = tpls(i, 2).uint_value (), err);
303         }
304       std::string name;
305       if (! err)
306         {
307           SET_ERR (name = tpls(i, 1).string_value (), err);
308         }
309       std::string nspace;
310       if (! err)
311         {
312           SET_ERR (nspace = tpls(i, 3).string_value (), err);
313         }
314       bool visible = false;
315       if (! err)
316         {
317           SET_ERR (visible = tpls(i, 4).bool_value (), err);
318         }
319       Cell r_el_oids;
320       if (! err)
321         {
322           SET_ERR (r_el_oids = tpls(i, 5).scalar_map_value ()
323                    .contents ("data").cell_value (), err);
324         }
325       Cell r_el_pos;
326       if (! err)
327         {
328           SET_ERR (r_el_pos = tpls(i, 6).scalar_map_value ()
329                    .contents ("data").cell_value (), err);
330         }
331       if (err)
332         {
333           _p_error ("octave_pq_get_composite_types: could not read returned result");
334           return 1;
335         }
336       octave_idx_type nel = r_el_oids.numel ();
337       if (nel != r_el_pos.numel ())
338         {
339           _p_error ("octave_pq_get_composite_types: internal error, inconsistent content of pg_attribute?");
340 
341           return 1;
342         }
343       oct_pq_el_oids_t el_oids;
344       el_oids.resize (nel);
345       oct_pq_conv_cache_t conv_cache;
346       conv_cache.resize (nel);
347       for (octave_idx_type i = 0; i < nel; i++)
348         {
349           octave_idx_type pos;
350           // "column" number (attnum) is one-based, so subtract 1
351           SET_ERR (pos = r_el_pos(i).idx_type_value () - 1, err);
352           if (! err)
353             {
354               if (pos >= nel)
355                 {
356                   _p_error ("octave_pq_get_composite_types: internal error (?system catalog erroneous?): column position %i greater than ncols %i for type %s, namespace %s",
357                          pos, nel, name.c_str (), nspace.c_str ());
358                   return 1;
359                 }
360 
361               SET_ERR (el_oids[pos] = r_el_oids(i).uint_value (), err);
362 
363               conv_cache[pos] = NULL;
364             }
365           if (err)
366             {
367               _p_error ("octave_pq_get_composite_types: could not fill in element oids.");
368 
369               return 1;
370             }
371         }
372 
373       // must be allocated and filled before creating the name map
374       // entry, to get a remaining location for the c-string used as
375       // key
376       oct_pq_conv_t *t_conv = new oct_pq_conv_t;
377       t_conv->oid = oid;
378       t_conv->aoid = aoid;
379       t_conv->el_oids = el_oids;
380       t_conv->conv_cache = conv_cache;
381       t_conv->is_composite = true;
382       t_conv->is_enum = false;
383       t_conv->is_not_constant = true;
384       t_conv->name = nspace.append (".").append (name);
385       t_conv->to_octave_str = NULL;
386       t_conv->to_octave_bin = NULL;
387       t_conv->from_octave_str = NULL;
388       t_conv->from_octave_bin = NULL;
389 
390       oct_pq_conv_t *&by_oid = conv_map[oid],
391         *&by_name = name_conv_map[t_conv->name.c_str ()];
392       if (by_oid || by_name)
393         {
394           _p_error ("octave_pq_get_composite_types: internal error, key already in typemap (by_oid: %u/%li, by name: %s/%li)",
395                  oid, by_oid, t_conv->name.c_str (), by_name);
396           if (! by_oid) conv_map.erase (oid);
397           if (! by_name) name_conv_map.erase (t_conv->name.c_str ());
398           delete t_conv;
399           return 1;
400         }
401 
402       by_oid = by_name = t_conv;
403 
404       oct_pq_conv_t *t_conv_v = NULL; // silence inadequate warning by
405                                       // initializing it here
406 
407       if (visible)
408         {
409           t_conv_v = new oct_pq_conv_t (*t_conv);
410 
411           t_conv_v->el_oids = el_oids;
412 
413           t_conv_v->conv_cache = conv_cache;
414 
415           t_conv_v->name = name;
416 
417           name_conv_map[t_conv_v->name.c_str ()] = t_conv_v;;
418         }
419 
420       if (aoid)
421         {
422           oct_pq_conv_t *&by_aoid = conv_map[aoid];
423           if (by_aoid)
424             {
425               _p_error ("octave_pq_get_composite_types: internal error, aoid key %u already in typemap", aoid);
426               conv_map.erase (oid);
427               name_conv_map.erase (t_conv->name.c_str ());
428               delete t_conv;
429               if (visible)
430                 {
431                   name_conv_map.erase (t_conv_v->name.c_str ());
432                   delete t_conv_v;
433                 }
434               return 1;
435             }
436 
437           by_aoid = by_oid;
438         }
439 
440     }
441 
442   return 0;
443 }
444 
octave_pq_get_enum_types(void)445 int octave_pq_connection_rep::octave_pq_get_enum_types (void)
446 {
447   Cell p, pt, rt;
448 
449   std::string cmd ("select pg_type.oid, pg_type.typname, pg_type.typarray, pg_namespace.nspname, pg_type_is_visible(pg_type.oid) from pg_type join pg_namespace on pg_type.typnamespace = pg_namespace.oid where pg_type.typtype = 'e';"),
450     caller ("octave_pq_get_enum_types");
451 
452   command c (*this, cmd, p, pt, rt, caller);
453   if (! c.good ())
454     {
455       _p_error ("octave_pq_get_enum_types: could not read pg_type");
456       return 1;
457     }
458 
459   octave_value res = c.process_single_result ();
460   if (! c.good ())
461     return 1;
462 
463   Cell tpls;
464   bool err;
465   SET_ERR (tpls = res.scalar_map_value ().contents ("data").cell_value (),
466            err);
467   if (err)
468     {
469       _p_error ("octave_pq_get_enum_types: could not convert result data to cell");
470       return 1;
471     }
472 
473   for (int i = 0; i < tpls.rows (); i++)
474     {
475       Oid oid;
476       SET_ERR (oid = tpls(i, 0).uint_value (), err);
477       Oid aoid;
478       if (! err)
479         {
480           SET_ERR (aoid = tpls(i, 2).uint_value (), err);
481         }
482       std::string name;
483       if (! err)
484         {
485           SET_ERR (name = tpls(i, 1).string_value (), err);
486         }
487       std::string nspace;
488       if (! err)
489         {
490           SET_ERR (nspace = tpls(i, 3).string_value (), err);
491         }
492       bool visible;
493       if (! err)
494         {
495           SET_ERR (visible = tpls(i, 4).bool_value (), err);
496         }
497       if (err)
498         {
499           _p_error ("octave_pq_get_enum_types: could not read returned result");
500           return 1;
501         }
502 
503       // must be allocated and filled before creating the name map
504       // entry, to get a remaining location for the c-string used as
505       // key
506       oct_pq_conv_t *t_conv = new oct_pq_conv_t;
507       t_conv->oid = oid;
508       t_conv->aoid = aoid;
509       t_conv->el_oids = oct_pq_el_oids_t ();
510       t_conv->conv_cache = oct_pq_conv_cache_t ();
511       t_conv->is_composite = false;
512       t_conv->is_enum = true;
513       t_conv->is_not_constant = true;
514       t_conv->name = nspace.append (".").append (name);
515       t_conv->to_octave_str = &to_octave_str_text;
516       t_conv->to_octave_bin = &to_octave_bin_text;
517       t_conv->from_octave_str = &from_octave_str_text;
518       t_conv->from_octave_bin = &from_octave_bin_text;
519 
520       // we trust there is always an array type in the table
521       oct_pq_conv_t *&by_oid = conv_map[oid], *&by_aoid = conv_map[aoid],
522         *&by_name = name_conv_map[t_conv->name.c_str ()];
523       if (by_oid || by_aoid || by_name)
524         {
525           _p_error ("octave_pq_get_enum_types: internal error, key already in typemap");
526           if (! by_oid) conv_map.erase (oid);
527           if (! by_aoid) conv_map.erase (aoid);
528           if (! by_name) name_conv_map.erase (t_conv->name.c_str ());
529           delete t_conv;
530           return 1;
531         }
532 
533       by_oid = by_aoid = by_name = t_conv;
534 
535       if (visible)
536         {
537           oct_pq_conv_t *t_conv_v = new oct_pq_conv_t (*t_conv);
538 
539           t_conv_v->el_oids = oct_pq_el_oids_t ();
540 
541           t_conv_v->conv_cache = oct_pq_conv_cache_t ();
542 
543           t_conv_v->name = name;
544 
545           name_conv_map[t_conv_v->name.c_str ()] = t_conv_v;
546         }
547     }
548 
549   return 0;
550 }
551 
octave_pq_refresh_types(void)552 int octave_pq_connection_rep::octave_pq_refresh_types (void)
553 {
554   octave_pq_delete_non_constant_types ();
555 
556   // refresh unqualified base type names, may be replaced later with
557   // user-defined types
558   for (int i = 0; i < OCT_PQ_NUM_CONVERTERS; i++)
559     name_conv_map[conv_ptrs[i]->name.c_str () + pq_bpl] = conv_ptrs[i];
560 
561   if (octave_pq_get_composite_types () || octave_pq_get_enum_types ())
562     {
563       PGconn *t_conn = conn;
564 
565       conn = NULL;
566 
567       if (t_conn)
568         {
569           BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
570           PQfinish (t_conn);
571           END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
572         }
573 
574       _p_error ("octave_pq_refresh_types: could not read types");
575       return 1;
576     }
577   else
578     return 0;
579 }
580