diff main/database/src/pq_connection.cc @ 11394:9aee227e296c octave-forge

Populated new database package with initial postgresql interface.
author i7tiol
date Wed, 16 Jan 2013 06:17:06 +0000
parents
children c20550232685
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/database/src/pq_connection.cc	Wed Jan 16 06:17:06 2013 +0000
@@ -0,0 +1,478 @@
+/*
+
+Copyright (C) 2012, 2013 Olaf Till <i7tiol@t-online.de>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "pq_connection.h"
+#include "command.h"
+
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_pq_connection, "PGconn", "PGconn")
+
+octave_pq_connection::octave_pq_connection (std::string arg)
+: postgres (0), conv_map (), name_conv_map (&map_str_cmp)
+{
+  static bool type_registered = false;
+
+  if (! type_registered) register_type ();
+
+  conn = PQconnectdb (arg.c_str ());
+
+  if (! conn || PQstatus (conn) == CONNECTION_BAD)
+    {
+      if (conn)
+        {
+          PQfinish (conn);
+
+          conn = NULL;
+        }
+
+      error ("PQ connection attempt failed");
+    }
+  else
+    {
+      // init name converter-map (kind of "bootstrapping")
+      for (int i = 0; i < OCT_PQ_NUM_CONVERTERS; i++)
+        name_conv_map[conv_ptrs[i]->name.c_str ()] = conv_ptrs[i];
+
+      if (octave_pq_get_postgres_oid () ||
+          octave_pq_fill_base_types () ||
+          octave_pq_get_composite_types () ||
+          octave_pq_get_enum_types ())
+        {
+          PQfinish (conn);
+
+          conn = NULL;
+
+          error ("could not read types");
+        }
+    }
+}
+
+octave_pq_connection::~octave_pq_connection (void)
+{
+  if (conn)
+    {
+      PQfinish (conn);
+
+      octave_pq_delete_non_constant_types ();
+    }
+}
+
+void octave_pq_connection::octave_pq_close (void)
+{
+  if (conn)
+    {
+      PQfinish (conn);
+
+      octave_pq_delete_non_constant_types ();
+
+      conn = NULL;
+    }
+  else
+    error ("PGconn object not open");
+}
+
+void octave_pq_connection::octave_pq_delete_non_constant_types (void)
+{
+  // In the first map, allocated types are usually referenced twice
+  // (by oid and aoid). Yet we need no refcount as long as we go
+  // through the name map as the last, since there (the same) types
+  // are only referenced once.
+
+  std::vector<oct_pq_conv_map_t::iterator> t_it_v;
+
+  for (oct_pq_conv_map_t::iterator it = conv_map.begin ();
+       it != conv_map.end ();
+       it++)
+    if (it->second->is_not_constant)
+      t_it_v.push_back (it);
+
+  for (std::vector<oct_pq_conv_map_t::iterator>::iterator it = t_it_v.begin ();
+       it != t_it_v.end (); it++)
+    conv_map.erase (*it);
+
+  std::vector<oct_pq_name_conv_map_t::iterator> t_name_it_v;
+
+  for (oct_pq_name_conv_map_t::iterator it = name_conv_map.begin ();
+       it != name_conv_map.end ();
+       it++)
+    {
+      oct_pq_conv_t *conv = it->second;
+
+      if (conv->is_not_constant)
+        {
+          t_name_it_v.push_back (it);
+
+          delete conv;
+        }
+    }
+
+  for (std::vector<oct_pq_name_conv_map_t::iterator>::iterator it =
+         t_name_it_v.begin ();
+       it != t_name_it_v.end (); it++)
+    name_conv_map.erase (*it);
+}
+
+int octave_pq_connection::octave_pq_get_postgres_oid (void)
+{
+  Cell p, pt, rt (1, 1);
+
+  rt(0) = octave_value ("oid");
+
+  std::string cmd ("select oid from pg_roles where rolname = 'postgres';"),
+    caller ("octave_pq_get_postgres_oid");
+
+  command c (*this, cmd, p, pt, rt, caller);
+  if (! c.good ())
+    {
+      error ("could not read pg_roles");
+      return 1;
+    }
+
+  octave_value res = c.process_single_result ();
+  if (error_state)
+    return 1;
+
+  postgres = res.scalar_map_value ().contents ("data").cell_value ()(0).
+    int_value ();
+  if (error_state)
+    return 1;
+
+  return 0;
+}
+
+int octave_pq_connection::octave_pq_fill_base_types (void)
+{
+  // assert postgres oid had been determined
+  if (! postgres.int_value ()) return 1;
+
+  Cell p (1, 1), pt (1, 1), rt (3, 1);
+  p(0) = postgres;
+  pt(0) = octave_value ("oid");
+  rt(0) = octave_value ("oid");
+  rt(1) = octave_value ("name");
+  rt(2) = octave_value ("oid");
+
+  std::string cmd ("select oid, typname, typarray from pg_type where (typowner = $1 AND typtype = 'b' AND typarray != 0) OR typname = 'record';"),
+    caller ("octave_pq_fill_base_types");
+
+  command c (*this, cmd, p, pt, rt, caller);
+  if (! c.good ())
+    {
+      error ("octave_pq_fill_base_types: could not read pg_type");
+      return 1;
+    }
+
+  octave_value res = c.process_single_result ();
+  if (error_state)
+    return 1;
+
+  Cell tpls = res.scalar_map_value ().contents ("data").cell_value ();
+  if (error_state)
+    {
+      error
+        ("octave_pq_fill_base_types: could not convert result data to cell");
+      return 1;
+    }
+
+  // make a temporary map of server base types (cell row numbers) for searching
+  typedef std::map<std::string, int, bool (*) (const std::string &,
+                                               const std::string &)>
+    bt_map_t;
+  bt_map_t bt_map (&map_string_cmp);
+  for (int i = 0; i < tpls.rows (); i++)
+    bt_map[tpls(i, 1).string_value ()] = i;
+  if (error_state)
+    {
+      error ("octave_pq_fill_base_types: could not read returned result");
+      return 1;
+    }
+
+  for (int i = 0; i < OCT_PQ_NUM_CONVERTERS; i++)
+    {
+      bt_map_t::iterator bt_it;
+      if ((bt_it = bt_map.find (conv_ptrs[i]->name)) == bt_map.end ())
+        {
+          error ("octave_pq_fill_base_types: type %s not found in pg_types",
+                 conv_ptrs[i]->name.c_str ());
+          return 1;
+        }
+      // fill in oid and aoid into static records of converters
+      conv_ptrs[i]->oid = tpls(bt_it->second, 0).int_value ();
+      conv_ptrs[i]->aoid = tpls(bt_it->second, 2).int_value ();
+
+      // fill in map of converters over oid with oid and, if not zero,
+      // also with aoid
+      conv_map[conv_ptrs[i]->oid] = conv_ptrs[i];
+      if (conv_ptrs[i]->aoid != 0)
+        conv_map[conv_ptrs[i]->aoid] = conv_ptrs[i];
+    }
+  if (error_state)
+    {
+      error ("octave_pq_fill_base_types: could not read returned result");
+      return 1;
+    }
+
+  return 0;
+}
+
+int octave_pq_connection::octave_pq_get_composite_types (void)
+{
+  Cell p, pt, rt (4, 1);
+  rt(0) = octave_value ("oid");
+  rt(1) = octave_value ("name");
+  rt(2) = octave_value ("oid");
+  rt(3) = octave_value ("oid");
+
+  std::string cmd ("select oid, typname, typarray, typrelid from pg_type where typtype = 'c';"),
+    caller ("octave_pq_get_composite_types");
+
+  command c (*this, cmd, p, pt, rt, caller);
+  if (! c.good ())
+    {
+      error ("octave_pq_get_composite_types: could not read pg_type");
+      return 1;
+    }
+
+  octave_value res = c.process_single_result ();
+  if (error_state)
+    return 1;
+
+  Cell tpls = res.scalar_map_value ().contents ("data").cell_value ();
+  if (error_state)
+    {
+      error ("octave_pq_get_composite_types: could not convert result data to cell");
+      return 1;
+    }
+
+  for (int i = 0; i < tpls.rows (); i++)
+    {
+      Oid oid = tpls(i, 0).int_value ();
+      Oid aoid = tpls(i, 2).int_value ();
+      Oid relid = tpls(i, 3).int_value ();
+      std::string name = tpls(i, 1).string_value ();
+      if (error_state)
+        {
+          error ("octave_pq_get_composite_types: could not read returned result");
+          return 1;
+        }
+
+      // must be allocated and filled before creating the name map
+      // entry, to get a remaining location for the c-string used as
+      // key
+      oct_pq_conv_t *t_conv = new oct_pq_conv_t;
+      t_conv->oid = oid;
+      t_conv->aoid = aoid;
+      t_conv->relid = relid;
+      t_conv->is_composite = true;
+      t_conv->is_enum = false;
+      t_conv->is_not_constant = true;
+      t_conv->name = name;
+      t_conv->to_octave_str = NULL;
+      t_conv->to_octave_bin = NULL;
+      t_conv->from_octave_str = NULL;
+      t_conv->from_octave_bin = NULL;
+
+      oct_pq_conv_t *&by_oid = conv_map[oid],
+        *&by_name = name_conv_map[t_conv->name.c_str ()];
+      if (by_oid || by_name)
+        {
+          error ("octave_pq_get_composite_types: internal error, key already in typemap (by_oid: %u/%li, by name: %s/%li)",
+                 oid, by_oid, t_conv->name.c_str (), by_name);
+          if (! by_oid) conv_map.erase (oid);
+          if (! by_name) name_conv_map.erase (t_conv->name.c_str ());
+          delete t_conv;
+          return 1;
+        }
+
+      by_oid = by_name = t_conv;
+
+      if (aoid)
+        {
+          oct_pq_conv_t *&by_aoid = conv_map[aoid];
+          if (by_aoid)
+            {
+              error ("octave_pq_get_composite_types: internal error, aoid key %u already in typemap", aoid);
+              conv_map.erase (oid);
+              name_conv_map.erase (t_conv->name.c_str ());
+              delete t_conv;
+              return 1;
+            }
+
+          by_aoid = by_oid;
+        }
+
+    }
+
+  return 0;
+}
+
+int octave_pq_connection::octave_pq_get_enum_types (void)
+{
+  Cell p, pt, rt (3, 1);
+  rt(0) = octave_value ("oid");
+  rt(1) = octave_value ("name");
+  rt(2) = octave_value ("oid");
+
+  std::string cmd ("select oid, typname, typarray from pg_type where typtype = 'e';"),
+    caller ("octave_pq_get_enum_types");
+
+  command c (*this, cmd, p, pt, rt, caller);
+  if (! c.good ())
+    {
+      error ("octave_pq_get_enum_types: could not read pg_type");
+      return 1;
+    }
+ 
+  octave_value res = c.process_single_result ();
+  if (error_state)
+    return 1;
+
+  Cell tpls = res.scalar_map_value ().contents ("data").cell_value ();
+  if (error_state)
+    {
+      error ("octave_pq_get_enum_types: could not convert result data to cell");
+      return 1;
+    }
+
+  for (int i = 0; i < tpls.rows (); i++)
+    {
+      Oid oid = tpls(i, 0).int_value ();
+      Oid aoid = tpls(i, 2).int_value ();
+      std::string name = tpls(i, 1).string_value ();
+      if (error_state)
+        {
+          error ("octave_pq_get_enum_types: could not read returned result");
+          return 1;
+        }
+
+      // must be allocated and filled before creating the name map
+      // entry, to get a remaining location for the c-string used as
+      // key
+      oct_pq_conv_t *t_conv = new oct_pq_conv_t;
+      t_conv->oid = oid;
+      t_conv->aoid = aoid;
+      t_conv->relid = 0;
+      t_conv->is_composite = false;
+      t_conv->is_enum = true;
+      t_conv->is_not_constant = true;
+      t_conv->name = name;
+      t_conv->to_octave_str = NULL;
+      t_conv->to_octave_bin = NULL;
+      t_conv->from_octave_str = NULL;
+      t_conv->from_octave_bin = NULL;
+
+      // we trust there is always an array type in the table
+      oct_pq_conv_t *&by_oid = conv_map[oid], *&by_aoid = conv_map[aoid],
+        *&by_name = name_conv_map[t_conv->name.c_str ()];
+      if (by_oid || by_aoid || by_name)
+        {
+          error ("octave_pq_get_enum_types: internal error, key already in typemap");
+          if (! by_oid) conv_map.erase (oid);
+          if (! by_aoid) conv_map.erase (aoid);
+          if (! by_name) name_conv_map.erase (t_conv->name.c_str ());
+          delete t_conv;
+          return 1;
+        }
+
+      by_oid = by_aoid = by_name = t_conv;
+
+    }
+
+  return 0;
+}
+
+int octave_pq_connection::octave_pq_refresh_types (void)
+{
+  octave_pq_delete_non_constant_types ();
+
+  if (octave_pq_get_composite_types () || octave_pq_get_enum_types ())
+    {
+      if (conn)
+        PQfinish (conn);
+      conn = NULL;
+      error ("octave_pq_refresh_types: could not read types");
+      return 1;
+    }
+  else
+    return 0;
+}
+
+int octave_pq_connection::octave_pq_get_cols (Oid relid, std::vector<Oid> &oids)
+{
+  // printf ("octave_pq_get_cols(relid %u): ", relid);
+
+  Cell p (1, 1), pt (1, 1), rt (2, 1);
+  p(0) = octave_value (octave_uint32 (relid));
+  pt(0) = octave_value ("oid");
+  rt(0) = octave_value ("oid");
+  rt(1) = octave_value ("int2");
+
+  std::string cmd ("select atttypid, attnum from pg_attribute where attrelid = $1;"),
+    caller ("octave_pq_get_cols");
+
+  command c (*this, cmd, p, pt, rt, caller);
+  if (! c.good ())
+    {
+      error ("%s: could not read pg_type", caller.c_str ());
+      return 1;
+    }
+
+  octave_value res = c.process_single_result ();
+  if (error_state)
+    return 1;
+
+  Cell tpls = res.scalar_map_value ().contents ("data").cell_value ();
+  if (error_state)
+    {
+      error
+        ("%s: could not convert result data to cell", caller.c_str ());
+      return 1;
+    }
+
+  octave_idx_type r = tpls.rows ();
+
+  // printf ("%i colums, ", r);
+
+  oids.resize (r);
+
+  // "column" number (attnum) is one-based, so subtract 1
+  for (octave_idx_type i = 0; i < r; i++)
+    {
+      octave_idx_type pos = tpls(i, 1).idx_type_value () - 1;
+
+      // printf ("%u at pos %i, ", tpls(i, 0).uint_value (), pos);
+
+      if (pos >= r)
+        {
+          error ("%s: internal error (?system catalog erroneous?): column position %i greater than ncols %i for relid %u",
+                 caller.c_str (), pos, r, relid);
+          return 1;
+        }
+
+      oids[pos] = tpls(i, 0).uint_value ();
+    }
+  if (error_state)
+    {
+      error ("%s: could not convert result data", caller.c_str ());
+      return 1;
+    }
+
+  // printf ("\n");
+
+  return 0;
+}