StringTypes has gone
[plcapi.git] / PLC / PostgreSQL.py
index eced84e..ae6c55a 100644 (file)
@@ -1,5 +1,5 @@
 #
-# PostgreSQL database interface. 
+# PostgreSQL database interface.
 # Sort of like DBI(3) (Database independent interface for Perl).
 #
 # Mark Huang <mlhuang@cs.princeton.edu>
@@ -13,13 +13,13 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
 psycopg2.extensions.register_type(psycopg2._psycopg.UNICODEARRAY)
 
 import types
-from types import StringTypes, NoneType
 import traceback
-import commands
+import subprocess
 import re
 from pprint import pformat
 
-from PLC.Debug import profile, log
+from PLC.Logger import logger
+from PLC.Debug import profile
 from PLC.Faults import *
 from datetime import datetime as DateTimeType
 
@@ -57,35 +57,62 @@ class PostgreSQL:
             self.connection.close()
             self.connection = None
 
+    @staticmethod
+    # From pgdb, and simplify code
+    def _quote(x):
+        if isinstance(x, DateTimeType):
+            x = str(x)
+        elif isinstance(x, str):
+            x = x.encode( 'utf-8' )
+
+        if isinstance(x, bytes):
+            x = "'%s'" % str(x).replace("\\", "\\\\").replace("'", "''")
+        elif isinstance(x, (int, float)):
+            pass
+        elif x is None:
+            x = 'NULL'
+        elif isinstance(x, (list, tuple, set)):
+            x = 'ARRAY[%s]' % ', '.join([str(_quote(x)) for x in x])
+        elif hasattr(x, '__pg_repr__'):
+            x = x.__pg_repr__()
+        else:
+            raise PLCDBError('Cannot quote type %s' % type(x))
+        return x
+
+
     def quote(self, value):
         """
         Returns quoted version of the specified value.
         """
-        # The pgdb._quote function is good enough for general SQL
-        # quoting, except for array types.
-        if isinstance (value, (types.ListType, types.TupleType, set)):
-            'ARRAY[%s]' % ', '.join( [ str(self.quote(x)) for x in value ] )
-        else:
-            try:
-                # up to PyGreSQL-3.x, function was pgdb._quote
-                import pgdb
-                return pgdb._quote(value)
-            except:
-                # with PyGreSQL-4.x, use psycopg2's adapt
-                from psycopg2.extensions import adapt
-                return adapt (value)
+        return PostgreSQL._quote (value)
+
+# following is an unsuccessful attempt to re-use lib code as much as possible
+#    def quote(self, value):
+#        # The pgdb._quote function is good enough for general SQL
+#        # quoting, except for array types.
+#        if isinstance (value, (types.ListType, types.TupleType, set)):
+#            'ARRAY[%s]' % ', '.join( [ str(self.quote(x)) for x in value ] )
+#        else:
+#            try:
+#                # up to PyGreSQL-3.x, function was pgdb._quote
+#                import pgdb
+#                return pgdb._quote(value)
+#            except:
+#                # with PyGreSQL-4.x, use psycopg2's adapt
+#                from psycopg2.extensions import adapt
+#                return adapt (value)
 
     @classmethod
     def param(self, name, value):
         # None is converted to the unquoted string NULL
-        if isinstance(value, NoneType):
+        if isinstance(value, type(None)):
             conversion = "s"
         # True and False are also converted to unquoted strings
         elif isinstance(value, bool):
             conversion = "s"
         elif isinstance(value, float):
             conversion = "f"
-        elif not isinstance(value, StringTypes):
+        elif not isinstance(value, str):
             conversion = "d"
         else:
             conversion = "s"
@@ -148,36 +175,33 @@ class PostgreSQL:
 
             if not params:
                 if self.debug:
-                    print >> log,'execute0',query
+                    logger.debug('execute0: {}'.format(query))
                 cursor.execute(query)
-            elif isinstance(params,dict):
+            elif isinstance(params, dict):
                 if self.debug:
-                    print >> log,'execute-dict: params',params,'query',query%params
-                cursor.execute(query,params)
+                    logger.debug('execute-dict: params {} query {}'
+                                 .format(params, query%params))
+                cursor.execute(query, params)
             elif isinstance(params,tuple) and len(params)==1:
                 if self.debug:
-                    print >> log,'execute-tuple',query%params[0]
+                    logger.debug('execute-tuple {}'.format(query%params[0]))
                 cursor.execute(query,params[0])
             else:
                 param_seq=(params,)
                 if self.debug:
                     for params in param_seq:
-                        print >> log,'executemany',query%params
+                        logger.debug('executemany {}'.format(query%params))
                 cursor.executemany(query, param_seq)
             (self.rowcount, self.description, self.lastrowid) = \
                             (cursor.rowcount, cursor.description, cursor.lastrowid)
-        except Exception, e:
+        except Exception as e:
             try:
                 self.rollback()
             except:
                 pass
-            uuid = commands.getoutput("uuidgen")
-            print >> log, "Database error %s:" % uuid
-            print >> log, e
-            print >> log, "Query:"
-            print >> log, query
-            print >> log, "Params:"
-            print >> log, pformat(params)
+            uuid = subprocess.getoutput("uuidgen")
+            message = "Database error {}: - Query {} - Params {}".format(uuid, query, pformat(params))
+            logger.exception(message)
             raise PLCDBError("Please contact " + \
                              self.api.config.PLC_NAME + " Support " + \
                              "<" + self.api.config.PLC_MAIL_SUPPORT_ADDRESS + ">" + \
@@ -204,7 +228,7 @@ class PostgreSQL:
             # Return each row as a dictionary keyed on field name
             # (like DBI selectrow_hashref()).
             labels = [column[0] for column in self.description]
-            rows = [dict(zip(labels, row)) for row in rows]
+            rows = [dict(list(zip(labels, row))) for row in rows]
 
         if key_field is not None and key_field in labels:
             # Return rows as a dictionary keyed on the specified field
@@ -219,7 +243,7 @@ class PostgreSQL:
         """
 
         if hasattr(self, 'fields_cache'):
-            if self.fields_cache.has_key((table, notnull, hasdef)):
+            if (table, notnull, hasdef) in self.fields_cache:
                 return self.fields_cache[(table, notnull, hasdef)]
         else:
             self.fields_cache = {}