Hi, Please find attached patch for RM 1983.
This issue only occurs when database encoding is other than utf-8 Also other issue was when we use connection of database with encoding other than utf-8 to retrieve data from cluster table/s which has encoding utf-8 (e.g. pg_database) then data was not decoded properly. -- *Harshal Dhumal* *Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company
diff --git a/web/pgadmin/browser/server_groups/servers/databases/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/__init__.py index 88360dd..df325e1 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/__init__.py @@ -9,6 +9,7 @@ """Implements the Database Node""" +import sys import simplejson as json import re from functools import wraps @@ -568,6 +569,24 @@ class DatabaseView(PGChildNodeView): ) data['old_name'] = (rset['rows'][0])['name'] + + # pg_database is cluster level table (with encoding utf-8) + # which is common to all databases under that cluster. + # So if connection of database with encoding other than utf-8 is + # used to query data from pg_database then manually decode + # database name to get correct one. + + if self.conn.py_encoding != 'utf_8': + if sys.version_info < (3,): + data['old_name'] = data['old_name']\ + .decode('utf-8')\ + .encode(self.conn.py_encoding)\ + .decode('utf-8') + else: + data['old_name'] = bytes( + data['old_name'], self.conn.py_encoding + ).decode('utf-8') + if 'name' not in data: data['name'] = data['old_name'] diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py index 0dc2d01..2224720 100644 --- a/web/pgadmin/browser/utils.py +++ b/web/pgadmin/browser/utils.py @@ -11,6 +11,7 @@ from abc import abstractmethod +import sys import flask from flask import render_template, current_app from flask_babel import gettext @@ -368,7 +369,24 @@ class PGChildNodeView(NodeView): current_app.logger.error(result) for row in result['rows']: + + # pg_database is cluster level table (with encoding utf-8) + # which is common to all databases under that cluster. + # So if connection of database with encoding other than utf-8 is + # used to query data from pg_database then manually decode + # dependencies name to get correct one. ref_name = row['refname'] + if conn.py_encoding != 'utf_8': + if sys.version_info < (3,): + ref_name = ref_name\ + .decode('utf-8')\ + .encode(conn.py_encoding)\ + .decode('utf-8') + else: + ref_name = bytes( + ref_name, conn.py_encoding + ).decode('utf-8') + dep_str = row['deptype'] dep_type = '' diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py index 4cafeb2..b442846 100644 --- a/web/pgadmin/utils/driver/psycopg2/__init__.py +++ b/web/pgadmin/utils/driver/psycopg2/__init__.py @@ -26,7 +26,7 @@ from flask import g, current_app, session from flask_babel import gettext from flask_security import current_user from pgadmin.utils.crypto import decrypt -from psycopg2.extensions import adapt +from psycopg2.extensions import adapt, encodings import config from pgadmin.model import Server, User @@ -74,6 +74,7 @@ psycopg2.extensions.register_type( 'NUMERIC_RANGE_TEXT', psycopg2.STRING) ) + def register_string_typecasters(connection): """ Casts various types to string, resolving issues with out of @@ -94,6 +95,30 @@ def register_string_typecasters(connection): new_type = psycopg2.extensions.new_type(oids, 'RETURN_STRING', return_as_string) psycopg2.extensions.register_type(new_type) + # In python3 when database encoding is other than utf-8 and client encoding + # is set to UNICODE then we need to map data from database encoding + # to utf-8. + # This is required because when client encoding is set to UNICODE then + # psycopg assumes database encoding utf-8 and not the actual encoding. + # Not sure whether it's bug or feature in psycopg for python3. + + if sys.version_info >= (3,) and connection.encoding != 'UTF8': + def return_as_unicode(value, cursor): + if value is None: + return None + # Treat value as byte sequence of database encoding and then decode + # it as utf-8 to get correct unicode value. + return bytes( + value, encodings[cursor.connection.encoding] + ).decode('utf-8') + + unicode_type = psycopg2.extensions.new_type( + (19, 18, 25, 1042, 1043, 0), + 'UNICODE', return_as_unicode) + + psycopg2.extensions.register_type(unicode_type) + + class Connection(BaseConnection): """ class Connection(object) @@ -183,6 +208,7 @@ class Connection(BaseConnection): self.manager = manager self.db = db if db is not None else manager.db self.conn = None + self.py_encoding = None self.auto_reconnect = auto_reconnect self.async = async self.__async_cursor = None @@ -345,6 +371,10 @@ Failed to connect to the database server(#{server_id}) for connection ({conn_id} self.conn_id.encode('utf-8') ), None) + # map postgres encoding string to python encoding string + # e.g UTF8 -> utf_8 similarly win1256 -> cp1256 etc. + self.py_encoding = encodings[self.conn.encoding] + status, cur = self.__cursor() formatted_exception_msg = self._formatted_exception_msg mgr = self.manager @@ -435,6 +465,25 @@ WHERE db.datname = current_database()""") res = cur.fetchmany(1)[0] mgr.db_info[res['did']] = res.copy() + # pg_database is cluster level table (with encoding utf-8) + # which is common to all databases under that cluster. + # So if connection of database with encoding other than utf-8 is + # used to query data from pg_database then manually decode + # database name to get correct one. + + if self.conn.encoding != 'UTF8': + if sys.version_info < (3,): + mgr.db_info[res['did']]['datname'] = mgr.db_info[ + res['did']]['datname']\ + .decode('utf-8')\ + .encode(self.py_encoding)\ + .decode('utf-8') + else: + mgr.db_info[res['did']]['datname'] = bytes( + mgr.db_info[res['did']]['datname'], + self.py_encoding + ).decode('utf-8') + # We do not have database oid for the maintenance database. if len(mgr.db_info) == 1: mgr.did = res['did'] @@ -568,6 +617,13 @@ WHERE query: SQL query to run. params: Extra parameters """ + + if sys.version_info < (3,): + if type(query) == unicode: + query = query.encode('utf-8') + else: + query = query.encode('utf-8') + cur.execute(query, params) if self.async == 1: self._wait(cur.connection) @@ -585,7 +641,7 @@ WHERE u"Execute (with server cursor) for server #{server_id} - {conn_id} (Query-id: {query_id}):\n{query}".format( server_id=self.manager.sid, conn_id=self.conn_id, - query=query, + query=query.decode('utf-8') if sys.version_info < (3,) else query, query_id=query_id ) ) @@ -703,6 +759,13 @@ WHERE params: extra parameters to the function formatted_exception_msg: if True then function return the formatted exception message """ + + if sys.version_info < (3,): + if type(query) == unicode: + query = query.encode('utf-8') + else: + query = query.encode('utf-8') + self.__async_cursor = None status, cur = self.__cursor() @@ -715,7 +778,7 @@ WHERE u"Execute (async) for server #{server_id} - {conn_id} (Query-id: {query_id}):\n{query}".format( server_id=self.manager.sid, conn_id=self.conn_id, - query=query, + query=query.decode('utf-8'), query_id=query_id ) ) @@ -733,7 +796,7 @@ Failed to execute query (execute_async) for the server #{server_id} - {conn_id} """.format( server_id=self.manager.sid, conn_id=self.conn_id, - query=query, + query=query.decode('utf-8'), errmsg=errmsg, query_id=query_id ) @@ -1421,7 +1484,8 @@ class ServerManager(object): database = self.db elif did in self.db_info: database = self.db_info[did]['datname'] - if hasattr(str, 'decode'): + if hasattr(str, 'decode') and \ + not isinstance(database, unicode): database = database.decode('utf-8') else: maintenance_db_id = u'DB:{0}'.format(self.db) @@ -1511,7 +1575,8 @@ WHERE db.oid = {0}""".format(did)) if did is not None: if did in self.db_info and 'datname' in self.db_info[did]: database = self.db_info[did]['datname'] - if hasattr(str, 'decode'): + if hasattr(str, 'decode') and \ + not isinstance(database, unicode): database = database.decode('utf-8') if database is None: return False
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers