This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository gnumed-server.

commit c270004c84fc413676f6ed3a5bf3f1560cd0ab63
Author: Andreas Tille <[email protected]>
Date:   Tue Sep 12 16:31:37 2017 +0200

    New upstream version 21.14
---
 server/bootstrap/bootstrap_gm_db_system.py         | 151 ++++++----
 server/doc/schema/gnumed-entire_schema.html        |   2 +-
 server/pycommon/gmPG2.py                           | 330 +++++++++++++++------
 server/pycommon/gmPsql.py                          |  61 ++--
 server/pycommon/x-test-default_ro.py               |  65 ++++
 server/pycommon/x-test-psy.py                      | 203 +++++++++++++
 .../v20-v21/dynamic/v21-release_notes-dynamic.sql  |  32 +-
 7 files changed, 654 insertions(+), 190 deletions(-)

diff --git a/server/bootstrap/bootstrap_gm_db_system.py 
b/server/bootstrap/bootstrap_gm_db_system.py
index ac3beed..a67a1c0 100755
--- a/server/bootstrap/bootstrap_gm_db_system.py
+++ b/server/bootstrap/bootstrap_gm_db_system.py
@@ -24,6 +24,8 @@ further details.
 --quiet
 --log-file=
 --conf-file=
+
+Requires psycopg 2.7 !
 """
 #==================================================================
 # TODO
@@ -36,7 +38,14 @@ __author__ = "[email protected]"
 __license__ = "GPL v2 or later"
 
 # standard library
-import sys, string, os.path, fileinput, os, time, getpass, glob, re as regex, 
tempfile
+import sys
+import os.path
+import fileinput
+import os
+import getpass
+import glob
+import re as regex
+import tempfile
 import io
 import logging
 
@@ -239,7 +248,7 @@ def create_db_group(cursor=None, group=None):
 
        return True
 #==================================================================
-def connect(host, port, db, user, passwd):
+def connect(host, port, db, user, passwd, conn_name=None):
        """
        This is a wrapper to the database connect function.
        Will try to recover gracefully from connection errors where possible
@@ -259,7 +268,7 @@ def connect(host, port, db, user, passwd):
        dsn = gmPG2.make_psycopg2_dsn(database=db, host=host, port=port, 
user=user, password=passwd)
        _log.info("trying DB connection to %s on %s as %s", db, host or 
'localhost', user)
        try:
-               conn = gmPG2.get_connection(dsn=dsn, readonly=False, 
pooled=False, verbose=True)
+               conn = gmPG2.get_connection(dsn=dsn, readonly=False, 
pooled=False, verbose=True, connection_name = conn_name)
        except:
                _log.exception(u'connection failed')
                raise
@@ -270,6 +279,7 @@ def connect(host, port, db, user, passwd):
 
        _log.info('successfully connected')
        return conn
+
 #==================================================================
 class user:
        def __init__(self, anAlias = None, aPassword = None):
@@ -372,7 +382,7 @@ class db_server:
                        if self.conn.closed == 0:
                                self.conn.close()
 
-               self.conn = connect(self.name, self.port, self.template_db, 
self.superuser.name, self.superuser.password)
+               self.conn = connect(self.name, self.port, self.template_db, 
self.superuser.name, self.superuser.password, conn_name = 
u'[email protected]')
                if self.conn is None:
                        _log.error('Cannot connect.')
                        return None
@@ -578,6 +588,7 @@ class database:
                return None
        #--------------------------------------------------------------
        def __bootstrap(self):
+
                global _dbowner
 
                # get owner
@@ -654,6 +665,7 @@ class database:
                #self.conn.close()
 
                return True
+
        #--------------------------------------------------------------
        def __connect_superuser_to_template(self):
                if self.conn is not None:
@@ -665,7 +677,8 @@ class database:
                        self.server.port,
                        self.template_db,
                        self.server.superuser.name,
-                       self.server.superuser.password
+                       self.server.superuser.password,
+                       conn_name = u'[email protected]'
                )
 
                self.conn.cookie = 'database.__connect_superuser_to_template'
@@ -675,6 +688,7 @@ class database:
                curs.close()
 
                return self.conn and 1
+
        #--------------------------------------------------------------
        def __connect_superuser_to_db(self):
                if self.conn is not None:
@@ -686,7 +700,8 @@ class database:
                        self.server.port,
                        self.name,
                        self.server.superuser.name,
-                       self.server.superuser.password
+                       self.server.superuser.password,
+                       conn_name = u'postgres@gnumed_vX'
                )
 
                self.conn.cookie = 'database.__connect_superuser_to_db'
@@ -749,7 +764,8 @@ class database:
                return self.conn and 1
        #--------------------------------------------------------------
        def __db_exists(self):
-               cmd = "BEGIN; SELECT datname FROM pg_database WHERE 
datname='%s'" % self.name
+               #cmd = "BEGIN; SELECT datname FROM pg_database WHERE 
datname='%s'" % self.name
+               cmd = "SELECT datname FROM pg_database WHERE datname='%s'" % 
self.name
 
                aCursor = self.conn.cursor()
                try:
@@ -790,16 +806,23 @@ class database:
                                print_msg("==> dropping pre-existing target 
database [%s] ..." % self.name)
                                _log.info('trying to drop target database')
                                cmd = 'DROP DATABASE "%s"' % self.name
-                               self.conn.set_isolation_level(0)
+                               _log.debug('committing existing connection 
before setting autocommit')
+                               self.conn.commit()
+                               _log.debug('setting autocommit to TRUE')
+                               self.conn.autocommit = True
+                               self.conn.readonly = False
                                cursor = self.conn.cursor()
                                try:
+                                       cursor.execute(u'SET 
default_transaction_read_only TO OFF')
+                                       _log.debug('running SQL: %s', cmd)
                                        cursor.execute(cmd)
                                except:
                                        _log.exception(">>>[%s]<<< failed" % 
cmd)
-                                       cursor.close()
+                                       _log.debug(u'conn state after failed 
DROP: %s', gmPG2.capture_conn_state(self.conn))
                                        return False
-                               cursor.close()
-                               self.conn.commit()
+                               finally:
+                                       cursor.close()
+                                       self.conn.set_session(readonly = False, 
autocommit = False)
                        else:
                                use_existing = bool(int(cfg_get(self.section, 
'use existing target database')))
                                if use_existing:
@@ -828,35 +851,36 @@ class database:
                                        tablespace = '%s'
                                ;""" % (self.name, self.owner.name, 
self.template_db, tablespace)
 
-               # create DB must be run outside transactions
-               old_iso = self.conn.isolation_level
-               self.conn.set_isolation_level(0)
-               cursor = self.conn.cursor()
-
                # get size
+               cursor = self.conn.cursor()
                size_cmd = "SELECT pg_size_pretty(pg_database_size('%s'))" % 
self.template_db
                cursor.execute(size_cmd)
                size = cursor.fetchone()[0]
+               cursor.close()
 
                # create database by cloning
                print_msg("==> cloning [%s] (%s) as target database [%s] ..." % 
(self.template_db, size, self.name))
+               # create DB must be run outside transactions
+               self.conn.commit()
+               self.conn.autocommit = True
+               self.conn.readonly = False
+               cursor = self.conn.cursor()
                try:
+                       cursor.execute(u'SET default_transaction_read_only TO 
OFF')
                        cursor.execute(create_db_cmd)
                except:
                        _log.exception(">>>[%s]<<< failed" % create_db_cmd)
-                       cursor.close()
-                       self.conn.set_isolation_level(old_iso)
                        return False
-               cursor.close()
-
-               self.conn.commit()
-               self.conn.set_isolation_level(old_iso)
+               finally:
+                       cursor.close()
+                       self.conn.set_session(readonly = False, autocommit = 
False)
 
                if not self.__db_exists():
                        return None
                _log.info("Successfully created GNUmed database [%s]." % 
self.name)
 
                return True
+
        #--------------------------------------------------------------
        def check_data_plausibility(self):
 
@@ -905,8 +929,8 @@ class database:
                        try:
                                tag, old_query = check_def.split('::::')
                        except:
-                               _log.exception('error in plausibility check, 
aborting')
-                               _log.error('check definition: %s', check_def)
+                               _log.exception(u'error in plausibility check, 
aborting')
+                               _log.error(u'check definition: %s', check_def)
                                print_msg("    ... failed (check definition 
error)")
                                all_tests_successful = False
                                continue
@@ -919,8 +943,8 @@ class database:
                                )
                                old_val = rows[0][0]
                        except:
-                               _log.exception('error in plausibility check 
[%s] (old), aborting' % tag)
-                               _log.error('SQL: %s', old_query)
+                               _log.exception(u'error in plausibility check 
[%s] (old), aborting' % tag)
+                               _log.error(u'SQL: %s', old_query)
                                print_msg("    ... failed (SQL error)")
                                all_tests_successful = False
                                continue
@@ -932,21 +956,21 @@ class database:
                                )
                                new_val = rows[0][0]
                        except:
-                               _log.exception('error in plausibility check 
[%s] (new), aborting' % tag)
-                               _log.error('SQL: %s', new_query)
+                               _log.exception(u'error in plausibility check 
[%s] (new), aborting' % tag)
+                               _log.error(u'SQL: %s', new_query)
                                print_msg("    ... failed (SQL error)")
                                all_tests_successful = False
                                continue
 
                        if new_val != old_val:
-                               _log.error('plausibility check [%s] failed, 
expected [%s], found [%s]' % (tag, old_val, new_val))
-                               _log.error('SQL (old DB): %s', old_query)
-                               _log.error('SQL (new DB): %s', new_query)
+                               _log.error(u'plausibility check [%s] failed, 
expected: %s (in old DB), found: %s (in new DB)' % (tag, old_val, new_val))
+                               _log.error(u'SQL (old DB): %s', old_query)
+                               _log.error(u'SQL (new DB): %s', new_query)
                                print_msg("    ... failed (data error, check 
[%s])" % tag)
                                all_tests_successful = False
                                continue
 
-                       _log.info('plausibility check [%s] succeeded' % tag)
+                       _log.info(u'plausibility check [%s] succeeded' % tag)
 
                template_conn.close()
                target_conn.close()
@@ -1028,8 +1052,10 @@ class database:
                        print_msg('')
                        print_msg('  
http://wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL')
                        print_msg('')
+
        #--------------------------------------------------------------
        def import_data(self):
+
                print_msg("==> upgrading reference data sets ...")
 
                import_scripts = cfg_get(self.section, "data import scripts")
@@ -1073,7 +1099,9 @@ class database:
 
        #--------------------------------------------------------------
        def verify_result_hash(self):
+
                print_msg("==> verifying target database schema ...")
+
                target_version = cfg_get(self.section, 'target version')
                if target_version == 'devel':
                        print_msg("    ... skipped (devel version)")
@@ -1096,32 +1124,46 @@ class database:
        def reindex_all(self):
 
                print_msg("==> reindexing target database (can take a while) 
...")
+
+               do_reindex = cfg_get(self.section, 'reindex')
+               if do_reindex is None:
+                       do_reindex = True
+               else:
+                       do_reindex = (int(do_reindex) == 1)
+               if not do_reindex:
+                       _log.warning('skipping REINDEXing')
+                       print_msg("    ... skipped")
+                       return True
+
                _log.info('REINDEXing cloned target database so upgrade does 
not fail in case of a broken index')
                _log.info('this may potentially take "quite a long time" 
depending on how much data there is in the database')
                _log.info('you may want to monitor the PostgreSQL log for signs 
of progress')
 
-               old_iso = self.conn.isolation_level
-               self.conn.set_isolation_level(0)
-               curs = self.conn.cursor()
+               self.conn.commit()
+               self.conn.set_session(readonly = False, autocommit = True)
+               curs_outer = self.conn.cursor()
+               curs_outer.execute(u'SET default_transaction_read_only TO OFF')
                cmd = 'REINDEX (VERBOSE) DATABASE %s' % self.name
                try:
-                       curs.execute(cmd)
+                       curs_outer.execute(cmd)
                except:
                        _log.exception(">>>[%s]<<< failed" % cmd)
-                       curs.close()
                        # re-attempt w/o VERBOSE
                        _log.info('attempting REINDEXing without VERBOSE')
-                       curs = self.conn.cursor()
+                       curs_inner = self.conn.cursor()
                        cmd = 'REINDEX DATABASE %s' % self.name
                        try:
-                               curs.execute(cmd)
+                               curs_inner.execute(cmd)
                        except:
                                _log.exception(">>>[%s]<<< failed" % cmd)
-                               curs.close()
-                               self.conn.set_isolation_level(old_iso)
                                return False
-               curs.close()
-               self.conn.set_isolation_level(old_iso)
+                       finally:
+                               curs_inner.close()
+                               self.conn.set_session(readonly = False, 
autocommit = False)
+               finally:
+                       curs_outer.close()
+                       self.conn.set_session(readonly = False, autocommit = 
False)
+
                return True
 
        #--------------------------------------------------------------
@@ -1335,6 +1377,7 @@ class database:
 
                self.conn.commit()
                return True
+
 #==================================================================
 class gmBundle:
        def __init__(self, aBundleAlias = None):
@@ -1494,6 +1537,7 @@ def ask_for_confirmation():
                else:
                        return None
        return True
+
 #--------------------------------------------------------------
 def _import_schema (group=None, schema_opt="schema", conn=None):
        # load schema
@@ -1517,14 +1561,21 @@ def _import_schema (group=None, schema_opt="schema", 
conn=None):
 
        # and import them
        psql = gmPsql.Psql(conn)
-       for file in schema_files:
-               the_file = os.path.join(schema_base_dir, file)
-               if psql.run(the_file) == 0:
-                       _log.info('successfully imported [%s]' % the_file)
-               else:
-                       _log.error('failed to import [%s]' % the_file)
-                       return None
+       for filename in schema_files:
+               if filename.strip() == u'':
+                       continue                                                
# skip empty line
+               if filename.startswith(u'# '):
+                       _log.info(filename)                             # log 
as comment
+                       continue
+               full_path = os.path.join(schema_base_dir, filename)
+               if psql.run(full_path) == 0:
+                       #_log.info('success')
+                       continue
+               _log.error(u'failure')
+               return None
+
        return True
+
 #------------------------------------------------------------------
 def exit_with_msg(aMsg = None):
        if aMsg is not None:
diff --git a/server/doc/schema/gnumed-entire_schema.html 
b/server/doc/schema/gnumed-entire_schema.html
index 581eaa2..6a174d8 100644
--- a/server/doc/schema/gnumed-entire_schema.html
+++ b/server/doc/schema/gnumed-entire_schema.html
@@ -112,7 +112,7 @@
   <body>
 
     <!-- Primary Index -->
-       <p><br><br>Dumped on 2017-05-14</p>
+       <p><br><br>Dumped on 2017-08-31</p>
 <h1><a name="index">Index of database - gnumed_v21</a></h1>
 <ul>
     
diff --git a/server/pycommon/gmPG2.py b/server/pycommon/gmPG2.py
index 4eb2bd6..36d2f87 100644
--- a/server/pycommon/gmPG2.py
+++ b/server/pycommon/gmPG2.py
@@ -35,7 +35,7 @@ from Gnumed.pycommon import gmDateTime
 from Gnumed.pycommon import gmBorg
 from Gnumed.pycommon import gmI18N
 from Gnumed.pycommon import gmLog2
-from Gnumed.pycommon.gmTools import prompted_input, u_replacement_character
+from Gnumed.pycommon.gmTools import prompted_input, u_replacement_character, 
format_dict_like
 
 _log = logging.getLogger('gm.db')
 
@@ -169,6 +169,30 @@ map_client_branch2required_db_version = {
        u'1.6': 21
 }
 
+map_psyco_tx_status2str = [
+       u'TRANSACTION_STATUS_IDLE',
+       u'TRANSACTION_STATUS_ACTIVE',
+       u'TRANSACTION_STATUS_INTRANS',
+       u'TRANSACTION_STATUS_INERROR',
+       u'TRANSACTION_STATUS_UNKNOWN'
+]
+
+map_psyco_conn_status2str = [
+       u'0 - ?',
+       u'STATUS_READY',
+       u'STATUS_BEGIN_ALIAS_IN_TRANSACTION',
+       u'STATUS_PREPARED'
+]
+
+map_psyco_iso_level2str = {
+       None: u'ISOLATION_LEVEL_DEFAULT (configured on server)',
+       0: u'ISOLATION_LEVEL_AUTOCOMMIT',
+       1: u'ISOLATION_LEVEL_READ_UNCOMMITTED',
+       2: u'ISOLATION_LEVEL_REPEATABLE_READ',
+       3: u'ISOLATION_LEVEL_SERIALIZABLE',
+       4: u'ISOLATION_LEVEL_READ_UNCOMMITTED'
+}
+
 # get columns and data types for a given table
 query_table_col_defs = u"""select
        cols.column_name,
@@ -442,6 +466,7 @@ def __request_login_params_tui():
                raise gmExceptions.ConnectionError(_("Cannot connect to 
database without login information!"))
 
        return login
+
 #---------------------------------------------------
 def __request_login_params_gui_wx():
        """GUI (wx) input request for database login parameters.
@@ -511,6 +536,7 @@ def make_psycopg2_dsn(database=None, host=None, port=5432, 
user=None, password=N
                dsn_parts.append('password=%s' % password)
 
        dsn_parts.append('sslmode=prefer')
+       dsn_parts.append('fallback_application_name=GNUmed')
 
        return ' '.join(dsn_parts)
 
@@ -878,10 +904,13 @@ def delete_translation_from_database(link_obj=None, 
language=None, original=None
        return True
 
 #------------------------------------------------------------------------
-def update_translation_in_database(language=None, original=None, 
translation=None):
-       cmd = u'SELECT i18n.upd_tx(%(lang)s, %(orig)s, %(trans)s)'
+def update_translation_in_database(language=None, original=None, 
translation=None, link_obj=None):
+       if language is None:
+               cmd = u'SELECT i18n.upd_tx(%(orig)s, %(trans)s)'
+       else:
+               cmd = u'SELECT i18n.upd_tx(%(lang)s, %(orig)s, %(trans)s)'
        args = {'lang': language, 'orig': original, 'trans': translation}
-       run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = 
False)
+       run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = 
False, link_obj = link_obj)
        return args
 
 #------------------------------------------------------------------------
@@ -1022,10 +1051,12 @@ def force_user_language(language=None):
 def send_maintenance_notification():
        cmd = u'notify "db_maintenance_warning"'
        run_rw_queries(queries = [{'cmd': cmd}], return_data = False)
+
 #------------------------------------------------------------------------
 def send_maintenance_shutdown():
        cmd = u'notify "db_maintenance_disconnect"'
        run_rw_queries(queries = [{'cmd': cmd}], return_data = False)
+
 #------------------------------------------------------------------------
 def is_pg_interval(candidate=None):
        cmd = u'SELECT %(candidate)s::interval'
@@ -1161,15 +1192,6 @@ def bytea2file_object(data_query=None, file_obj=None, 
chunk_size=0, data_size=No
        needed_chunks, remainder = divmod(data_size, chunk_size)
        _log.debug('# of chunks: %s; remainder: %s bytes', needed_chunks, 
remainder)
 
-#      # since we now require PG 9.1 we can disable this workaround:
-#      # try setting "bytea_output"
-#      # - fails if not necessary
-#      # - succeeds if necessary
-#      try:
-#              run_ro_queries(link_obj = conn, queries = [{'cmd': u"set 
bytea_output to 'escape'"}])
-#      except dbapi.ProgrammingError:
-#              _log.debug('failed to set bytea_output to "escape", not 
necessary')
-
        # retrieve chunks, skipped if data size < chunk size,
        # does this not carry the danger of cutting up multi-byte escape 
sequences ?
        # no, since bytea is binary,
@@ -1468,6 +1490,27 @@ def file2bytea_overlay(query=None, args=None, 
filename=None, conn=None, md5_quer
        _log.error('MD5 sums of data file and database BYTEA field do not 
match: [file::%s] <> [DB::%s]', file_md5, db_md5)
        return False
 
+#---------------------------------------------------------------------------
+def run_sql_script(sql_script, conn=None):
+
+       if conn is None:
+               conn = get_connection(readonly = False)
+
+       from Gnumed.pycommon import gmPsql
+       psql = gmPsql.Psql(conn)
+
+       if psql.run(sql_script) == 0:
+               query = {
+                       'cmd': u'select gm.log_script_insertion(%(name)s, 
%(ver)s)',
+                       'args': {'name': sql_script, 'ver': u'current'}
+               }
+               run_rw_queries(link_obj = conn, queries = [query])
+               conn.commit()
+               return True
+
+       _log.error('error running sql script: %s', sql_script)
+       return False
+
 #------------------------------------------------------------------------
 def sanitize_pg_regex(expression=None, escape_all=False):
        """Escape input for use in a PostgreSQL regular expression.
@@ -1496,6 +1539,56 @@ def sanitize_pg_regex(expression=None, escape_all=False):
                #']', '\]',                     # not needed
 
 #------------------------------------------------------------------------
+def capture_conn_state(conn=None):
+
+       tx_status = conn.get_transaction_status()
+       if tx_status in [ psycopg2.extensions.TRANSACTION_STATUS_INERROR, 
psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN ]:
+               isolation_level = u'%s (tx aborted or unknown, cannot 
retrieve)' % conn.isolation_level
+       else:
+               isolation_level = u'%s (%s)' % (conn.isolation_level, 
map_psyco_iso_level2str[conn.isolation_level])
+       conn_status = u'%s (%s)' % (conn.status, 
map_psyco_conn_status2str[conn.status])
+       if conn.closed != 0:
+               conn_status = u'undefined (%s)' % conn_status
+
+       d = {
+               u'identity': id(conn),
+               u'backend PID': conn.get_backend_pid(),
+               u'protocol version': conn.protocol_version,
+               u'encoding': conn.encoding,
+               u'closed': conn.closed,
+               u'readonly': conn.readonly,
+               u'autocommit': conn.autocommit,
+               u'isolation level (psyco)': isolation_level,
+               u'async': conn.async,
+               u'deferrable': conn.deferrable,
+               u'transaction status': u'%s (%s)' % (tx_status, 
map_psyco_tx_status2str[tx_status]),
+               u'connection status': conn_status,
+               u'executing async op': conn.isexecuting(),
+               u'type': type(conn)
+       }
+       return u'%s\n' % conn + format_dict_like (
+               d,
+               relevant_keys = [
+                       u'type',
+                       u'identity',
+                       u'backend PID',
+                       u'protocol version',
+                       u'encoding',
+                       u'isolation level (psyco)',
+                       u'readonly',
+                       u'autocommit',
+                       u'closed',
+                       u'connection status',
+                       u'transaction status',
+                       u'deferrable',
+                       u'async',
+                       u'executing async op'
+               ],
+               tabular = True,
+               value_delimiters = None
+       )
+
+#------------------------------------------------------------------------
 def capture_cursor_state(cursor=None):
        conn = cursor.connection
 
@@ -1505,6 +1598,11 @@ def capture_cursor_state(cursor=None):
        else:
                isolation_level = conn.isolation_level
 
+       if cursor.query is None:
+               query = u'<no query>'
+       else:
+               query = unicode(cursor.query, 'utf8', 'replace')
+
        txt = u"""Link state:
 Cursor
   identity: %s; name: %s
@@ -1514,7 +1612,7 @@ Cursor
   statusmessage: %s
 Connection
   identity: %s; backend pid: %s; protocol version: %s;
-  closed: %s; autocommit: %s; isolation level: %s; encoding: %s; async: %s;
+  closed: %s; autocommit: %s; isolation level: %s; encoding: %s; async: %s; 
deferrable: %s; readonly: %s;
   TX status: %s; CX status: %s; executing async op: %s;
 Query
   %s
@@ -1540,11 +1638,13 @@ Query
                isolation_level,
                conn.encoding,
                conn.async,
-               tx_status,
-               conn.status,
+               conn.deferrable,
+               conn.readonly,
+               map_psyco_tx_status2str[tx_status],
+               map_psyco_conn_status2str[conn.status],
                conn.isexecuting(),
 
-               unicode(cursor.query, 'utf8', 'replace'),
+               query
        )
        return txt
 
@@ -1602,6 +1702,14 @@ def run_ro_queries(link_obj=None, queries=None, 
verbose=False, return_data=True,
                except dbapi.Error as pg_exc:
                        _log.error('query failed in RO connection')
                        _log.error(capture_cursor_state(curs))
+                       if hasattr(pg_exc, 'diag'):
+                               for prop in dir(pg_exc.diag):
+                                       if prop.startswith(u'__'):
+                                               continue
+                                       val = getattr(pg_exc.diag, prop)
+                                       if val is None:
+                                               continue
+                                       _log.error(u'PG diags %s: %s', prop, 
val)
                        pg_exc = make_pg_exception_fields_unicode(pg_exc)
                        _log.error('PG error code: %s', pg_exc.pgcode)
                        if pg_exc.pgerror is not None:
@@ -1650,6 +1758,9 @@ def run_ro_queries(link_obj=None, queries=None, 
verbose=False, return_data=True,
                        col_idx = get_col_indices(curs)
 
        curs_close()
+       # so we can see data committed meanwhile if the
+       # link object had been passed in and thusly might
+       # be part of a long-running read-only transaction
        readonly_rollback_just_in_case()
        return (data, col_idx)
 
@@ -1748,6 +1859,14 @@ def run_rw_queries(link_obj=None, queries=None, 
end_tx=False, return_data=None,
                except dbapi.Error as pg_exc:
                        _log.error('query failed in RW connection')
                        _log.error(capture_cursor_state(curs))
+                       if hasattr(pg_exc, 'diag'):
+                               for prop in dir(pg_exc.diag):
+                                       if prop.startswith(u'__'):
+                                               continue
+                                       val = getattr(pg_exc.diag, prop)
+                                       if val is None:
+                                               continue
+                                       _log.error(u'PG diags %s: %s', prop, 
val)
                        for notice in notices_accessor.notices:
                                _log.error(notice.strip(u'\n').strip(u'\r'))
                        del notices_accessor.notices[:]
@@ -1906,7 +2025,7 @@ class 
cConnectionPool(psycopg2.pool.PersistentConnectionPool):
                        self._used[conn_key].original_close()
 
 # -----------------------------------------------------------------------
-def get_raw_connection(dsn=None, verbose=False, readonly=True):
+def get_raw_connection(dsn=None, verbose=False, readonly=True, 
connection_name=None, autocommit=False):
        """Get a raw, unadorned connection.
 
        - this will not set any parameters such as encoding, timezone, datestyle
@@ -1921,9 +2040,21 @@ def get_raw_connection(dsn=None, verbose=False, 
readonly=True):
        if u'host=salaam.homeunix' in dsn:
                raise ValueError('The public database is not hosted by 
<salaam.homeunix.com> anymore.\n\nPlease point your configuration files to 
<publicdb.gnumed.de>.')
 
+       # try to enforce a useful encoding early on so that we
+       # have a good chance of decoding authentication errors
+       # containing foreign language characters
+       if u' client_encoding=' not in dsn:
+               dsn += u' client_encoding=utf8'
+
+       if connection_name is None:
+               if u' application_name' not in dsn:
+                       dsn += u" application_name=GNUmed"
+       else:
+               if u' application_name' not in dsn:
+                       dsn += u" application_name=%s" % connection_name
+
        try:
                conn = dbapi.connect(dsn=dsn, 
connection_factory=psycopg2.extras.DictConnection)
-               #conn = dbapi.connect(dsn=dsn, 
cursor_factory=psycopg2.extras.RealDictCursor)
        except dbapi.OperationalError, e:
 
                t, v, tb = sys.exc_info()
@@ -1932,22 +2063,27 @@ def get_raw_connection(dsn=None, verbose=False, 
readonly=True):
                except (AttributeError, IndexError, TypeError):
                        raise
 
-               msg = unicode(msg, gmI18N.get_encoding(), 'replace')
+               #msg = unicode(msg, gmI18N.get_encoding(), 'replace')
+               msg = unicode(msg, u'utf8', 'replace')
 
-               if msg.find('fe_sendauth') != -1:
+               if u'fe_sendauth' in msg:
                        raise cAuthenticationError, (dsn, msg), tb
 
                if regex.search('user ".*" does not exist', msg) is not None:
                        raise cAuthenticationError, (dsn, msg), tb
 
-               if msg.find('uthenti') != -1:
+               if u'uthenti' in msg:
                        raise cAuthenticationError, (dsn, msg), tb
 
                raise
 
-       _log.debug('new database connection, backend PID: %s, readonly: %s', 
conn.get_backend_pid(), readonly)
+       if connection_name is None:
+               _log.debug('established anonymous database connection, backend 
PID: %s', conn.get_backend_pid())
+       else:
+               _log.debug('established database connection "%s", backend PID: 
%s', connection_name, conn.get_backend_pid())
 
-       # do first-time stuff
+       # do first-connection-only stuff
+       # - verify PG version
        global postgresql_version
        if postgresql_version is None:
                curs = conn.cursor()
@@ -1967,44 +2103,30 @@ def get_raw_connection(dsn=None, verbose=False, 
readonly=True):
                except:
                        pass
                if verbose:
-                       _log_PG_settings(curs=curs)
+                       _log_PG_settings(curs = curs)
                curs.close()
                conn.commit()
 
+       # - verify PG understands client time zone
        if _default_client_timezone is None:
                __detect_client_timezone(conn = conn)
 
-       curs = conn.cursor()
-
-       # set access mode
-       conn.set_session(readonly = readonly)
-       conn.set_session(autocommit = readonly)
-       if readonly:
-               _log.debug('access mode [READ ONLY]')
-               #conn.set_session(readonly = True)
-               _log.debug('readonly: autocommit=True to avoid <IDLE IN 
TRANSACTION>')
-#              conn.autocommit = True
-#              cmd = 'set session characteristics as transaction READ ONLY'
-#              curs.execute(cmd)
-#              cmd = 'set default_transaction_read_only to on'
-#              curs.execute(cmd)
+       # - set access mode
+       if readonly is True:
+               _log.debug('readonly: forcing autocommit=True to avoid <IDLE IN 
TRANSACTION>')
+               autocommit = True
        else:
-               _log.debug('access mode [READ WRITE]')
-#              conn.set_session(readonly = False)
-               _log.debug('readwrite: autocommit=False')
-#              cmd = 'set session characteristics as transaction READ WRITE'
-#              curs.execute(cmd)
-#              cmd = 'set default_transaction_read_only to off'
-#              curs.execute(cmd)
-
-       curs.close()
+               _log.debug('autocommit is desired to be: %s', autocommit)
        conn.commit()
+       conn.autocommit = autocommit
+       conn.readonly = readonly
 
        conn.is_decorated = False
 
        return conn
+
 # =======================================================================
-def get_connection(dsn=None, readonly=True, encoding=None, verbose=False, 
pooled=True):
+def get_connection(dsn=None, readonly=True, encoding=None, verbose=False, 
pooled=True, connection_name=None, autocommit=False):
        """Get a new connection.
 
        This assumes the locale system has been initialized
@@ -2015,15 +2137,20 @@ def get_connection(dsn=None, readonly=True, 
encoding=None, verbose=False, pooled
        if pooled and readonly and (dsn is None):
                global __ro_conn_pool
                if __ro_conn_pool is None:
+                       log_ro_conn = True
                        __ro_conn_pool = cConnectionPool (
                                minconn = 1,
                                maxconn = 2,
                                dsn = dsn,
                                verbose = verbose
                        )
+               else:
+                       log_ro_conn = False
                conn = __ro_conn_pool.getconn()
+               if log_ro_conn:
+                       [ _log.debug(line) for line in capture_conn_state(conn 
= conn).split(u'\n') ]
        else:
-               conn = get_raw_connection(dsn=dsn, verbose=verbose, 
readonly=False)
+               conn = get_raw_connection(dsn = dsn, verbose = verbose, 
readonly = readonly, connection_name = connection_name, autocommit = autocommit)
 
        if conn.is_decorated:
                return conn
@@ -2048,57 +2175,42 @@ def get_connection(dsn=None, readonly=True, 
encoding=None, verbose=False, pooled
 
        # - transaction isolation level
        if readonly:
-               # alter-database default, checked at connect, no need to set now
-               iso_level = u'read committed'
+               # alter-database default, checked at connect, no need to set 
here
+               pass
        else:
                
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
-               iso_level = u'serializable'
-
-       _log.debug('client string encoding [%s], isolation level [%s], time 
zone [%s]', encoding, iso_level, _default_client_timezone)
 
-       curs = conn.cursor()
+       _log.debug('client time zone [%s]', _default_client_timezone)
 
        # - client time zone
+       curs = conn.cursor()
        curs.execute(_sql_set_timezone, [_default_client_timezone])
-
-       conn.commit()
-
-#      # FIXME: remove this whole affair once either 9.0 is standard (Ubuntu 
10 LTS is
-#      # FIXME: PG 8.4, however!) or else when psycopg2 supports a workaround
-#      #
-#      # - bytea data format
-#      # PG 9.0 switched to - by default - using "hex" rather than "escape",
-#      # however, psycopg2's linked with a pre-9.0 libpq do assume "escape"
-#      # as the transmission mode for bytea output,
-#      # so try to set this setting back to "escape",
-#      # if that's not possible the reason will be that PG < 9.0 does not 
support
-#      # that setting - which also means we don't need it and can ignore the
-#      # failure
-#      cmd = "set bytea_output to 'escape'"
-#      try:
-#              curs.execute(cmd)
-#      except dbapi.ProgrammingError:
-#              _log.error('cannot set bytea_output format')
-
        curs.close()
        conn.commit()
 
        conn.is_decorated = True
 
+       if verbose:
+               [ _log.debug(line) for line in capture_conn_state(conn = 
conn).split(u'\n') ]
+
        return conn
+
 #-----------------------------------------------------------------------
 def shutdown():
        if __ro_conn_pool is None:
                return
        __ro_conn_pool.shutdown()
+
 # ======================================================================
 # internal helpers
 #-----------------------------------------------------------------------
 def __noop():
        pass
+
 #-----------------------------------------------------------------------
 def _raise_exception_on_ro_conn_close():
        raise TypeError(u'close() called on read-only connection')
+
 #-----------------------------------------------------------------------
 def log_database_access(action=None):
        run_insert (
@@ -2107,6 +2219,7 @@ def log_database_access(action=None):
                values = {u'user_action': action},
                end_tx = True
        )
+
 #-----------------------------------------------------------------------
 def sanity_check_time_skew(tolerance=60):
        """Check server time and local time to be within
@@ -2150,6 +2263,7 @@ def sanity_check_time_skew(tolerance=60):
                return False
 
        return True
+
 #-----------------------------------------------------------------------
 def sanity_check_database_settings():
        """Checks database settings.
@@ -2237,22 +2351,43 @@ def sanity_check_database_settings():
                return 1, u'\n'.join(msg)
 
        return 0, u''
+
 #------------------------------------------------------------------------
 def _log_PG_settings(curs=None):
-       # don't use any of the run_*()s since that might
-       # create a loop if we fail here
-       # FIXME: use pg_settings
+       # don't use any of the run_*()s helper functions
+       # since that might create a loop if we fail here
        try:
-               curs.execute(u'show all')
+               # .pending_restart does not exist in PG 9.4 yet
+               #curs.execute(u'SELECT name, setting, unit, source, reset_val, 
sourcefile, sourceline, pending_restart FROM pg_settings')
+               curs.execute(u'SELECT name, setting, unit, source, reset_val, 
sourcefile, sourceline FROM pg_settings')
        except:
-               _log.exception(u'cannot log PG settings (>>>show all<<< 
failed)')
+               _log.exception(u'cannot log PG settings ("SELECT ... FROM 
pg_settings" failed)')
                return False
        settings = curs.fetchall()
-       if settings is None:
-               _log.error(u'cannot log PG settings (>>>show all<<< did not 
return rows)')
-               return False
        for setting in settings:
-               _log.debug(u'PG option [%s]: %s', setting['name'], 
setting['setting'])
+               if setting['unit'] is None:
+                       unit = u''
+               else:
+                       unit = u' %s' % setting['unit']
+               if setting['sourcefile'] is None:
+                       sfile = u''
+               else:
+                       sfile = u'// %s @ %s' % (setting['sourcefile'], 
setting['sourceline'])
+#              # .pending_restart does not exist in PG 9.4 yet
+#              if setting['pending_restart'] is False:
+#                      pending_restart = u''
+#              else:
+#                      pending_restart = u'// needs restart'
+#              _log.debug(u'%s: %s%s (set from: [%s] // sess RESET will set 
to: [%s]%s%s)',
+               _log.debug(u'%s: %s%s (set from: [%s] // sess RESET will set 
to: [%s]%s)',
+                       setting['name'],
+                       setting['setting'],
+                       unit,
+                       setting['source'],
+                       setting['reset_val'],
+#                      pending_restart,
+                       sfile
+               )
 
        try:
                curs.execute(u'select pg_available_extensions()')
@@ -2266,7 +2401,19 @@ def _log_PG_settings(curs=None):
        for ext in extensions:
                _log.debug(u'PG extension: %s', ext['pg_available_extensions'])
 
+       # not really that useful because:
+       # - clusterwide
+       # - not retained across server restart (fixed in 9.6.1 - really ?)
+#      try:
+#              curs.execute(u'SELECT pg_last_committed_xact()')
+#      except:
+#              _log.exception(u'cannot retrieve last committed xact')
+#      xact = curs.fetchall()
+#      if xact is not None:
+#              _log.debug(u'last committed transaction in cluster: %s', 
xact[0])
+
        return True
+
 #========================================================================
 def make_pg_exception_fields_unicode(exc):
 
@@ -2286,6 +2433,7 @@ def make_pg_exception_fields_unicode(exc):
        exc.u_pgerror = unicode(exc.pgerror, gmI18N.get_encoding(), 
'replace').strip().strip(u'\n').strip().strip(u'\n')
 
        return exc
+
 #------------------------------------------------------------------------
 def extract_msg_from_pg_exception(exc=None):
 
@@ -2296,6 +2444,7 @@ def extract_msg_from_pg_exception(exc=None):
 
        # assumption
        return unicode(msg, gmI18N.get_encoding(), 'replace')
+
 # =======================================================================
 class cAuthenticationError(dbapi.OperationalError):
 
@@ -2719,6 +2868,7 @@ if __name__ == "__main__":
        #--------------------------------------------------------------------
        def test_sanity_check_time_skew():
                sanity_check_time_skew()
+
        #--------------------------------------------------------------------
        def test_get_foreign_key_names():
                print get_foreign_key_names (
@@ -2729,6 +2879,7 @@ if __name__ == "__main__":
                        target_table = u'episode',
                        target_column = u'pk'
                )
+
        #--------------------------------------------------------------------
        def test_get_foreign_key_details():
                for row in get_foreign_keys2column (
@@ -2743,6 +2894,7 @@ if __name__ == "__main__":
                                row['referenced_table'],
                                row['referenced_column']
                        )
+
        #--------------------------------------------------------------------
        def test_set_user_language():
                # (user, language, result, exception type)
@@ -2772,10 +2924,12 @@ if __name__ == "__main__":
                                print "test:", test
                                print "expected exception"
                                print "result:", e
+
        #--------------------------------------------------------------------
        def test_get_schema_revision_history():
                for line in get_schema_revision_history():
                        print u' - '.join(line)
+
        #--------------------------------------------------------------------
        def test_run_query():
                gmDateTime.init()
@@ -2856,6 +3010,11 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
                run_rw_queries(queries = [{'cmd': u'SELEC 1'}])
 
        #--------------------------------------------------------------------
+       def test_log_settings():
+               conn = conn = get_connection()
+               _log_PG_settings(curs = conn.cursor())
+
+       #--------------------------------------------------------------------
        # run tests
        #test_get_connection()
        #test_exceptions()
@@ -2881,5 +3040,6 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
        #test_file2bytea_copy_from()
        #test_file2bytea_lo()
        test_faulty_SQL()
+       #test_log_settings()
 
 # ======================================================================
diff --git a/server/pycommon/gmPsql.py b/server/pycommon/gmPsql.py
index 46df8c1..ad1b963 100644
--- a/server/pycommon/gmPsql.py
+++ b/server/pycommon/gmPsql.py
@@ -41,6 +41,7 @@ class Psql:
                """
                self.conn = conn
                self.vars = {'ON_ERROR_STOP':None}
+
        #---------------------------------------------------------------
        def match (self, str):
                match = re.match (str, self.line)
@@ -50,6 +51,7 @@ class Psql:
                        ret = 1
                        self.groups = match.groups ()
                return ret
+
        #---------------------------------------------------------------
        def fmt_msg(self, aMsg):
                try:
@@ -65,6 +67,7 @@ class Psql:
                        except: pass
                        unformattable_error_id += 1
                return tmp
+
        #---------------------------------------------------------------
        def run (self, filename):
                """
@@ -89,8 +92,8 @@ class Psql:
                in_string = False
                bracketlevel = 0
                curr_cmd = ''
-               curs = self.conn.cursor ()
-##             transaction_started = False
+               curs = self.conn.cursor()
+
                for self.line in self.file.readlines():
                        self.lineno += 1
                        if len(self.line.strip()) == 0:
@@ -100,20 +103,24 @@ class Psql:
                        if self.match (r"^\\echo (.*)"):
                                _log.info(self.fmt_msg(shell(self.groups[0])))
                                continue
+
                        # \qecho
                        if self.match (r"^\\qecho (.*)"):
                                _log.info(self.fmt_msg(shell (self.groups[0])))
                                continue
+
                        # \q
                        if self.match (r"^\\q"):
                                _log.warning(self.fmt_msg(u"script terminated 
by \\q"))
                                return 0
+
                        # \set
                        if self.match (r"^\\set (\S+) (\S+)"):
                                self.vars[self.groups[0]] = shell 
(self.groups[1])
                                if self.groups[0] == 'ON_ERROR_STOP':
                                        self.vars['ON_ERROR_STOP'] = int 
(self.vars['ON_ERROR_STOP'])
                                continue
+
                        # \unset
                        if self.match (r"^\\unset (\S+)"):
                                self.vars[self.groups[0]] = None
@@ -150,46 +157,26 @@ class Psql:
                                        curr_cmd += this_char
                                else:
                                        try:
-#                                              if curr_cmd.strip ().upper () 
== 'COMMIT':
-#                                                      if transaction_started:
-#                                                              
self.conn.commit ()
-#                                                              curs.close ()
-#                                                              curs = 
self.conn.cursor ()
-#                                                              
_log.debug(self.fmt_msg ("transaction committed"))
-#                                                      else:
-#                                                              
_log.warning(self.fmt_msg ("COMMIT without BEGIN: no actual transaction 
happened!"))
-#                                                      transaction_started = 
False
-
-#                                              elif curr_cmd.strip ().upper () 
== 'BEGIN':
-#                                                      if transaction_started:
-#                                                              
_log.warning(self.fmt_msg ("BEGIN inside transaction"))
-#                                                      else:
-#                                                              
transaction_started = True
-#                                                              
_log.debug(self.fmt_msg ("starting transaction"))
-
-#                                              else:
                                                if curr_cmd.strip() != '':
-                                                       if 
curr_cmd.find('vacuum'):
-                                                               
self.conn.commit();
-                                                               curs.close()
-                                                               old_iso_level = 
self.conn.isolation_level
-                                                               
self.conn.set_isolation_level(0)
-                                                               curs = 
self.conn.cursor()
-                                                               curs.execute 
(curr_cmd)
-                                                               
self.conn.set_isolation_level(old_iso_level)
-                                                       else:
-                                                               curs.execute 
(curr_cmd)
-#                                                              if not 
transaction_started:
-                                       except Exception, error:
-                                               _log.debug(curr_cmd)
+                                                       curs.execute (curr_cmd)
+                                       except Exception as error:
+                                               _log.exception(curr_cmd)
                                                if re.match (r"^NOTICE:.*", 
str(error)):
                                                        
_log.warning(self.fmt_msg(error))
                                                else:
+                                                       
_log.error(self.fmt_msg(error))
+                                                       if hasattr(error, 
'diag'):
+                                                               for prop in 
dir(error.diag):
+                                                                       if 
prop.startswith(u'__'):
+                                                                               
continue
+                                                                       val = 
getattr(error.diag, prop)
+                                                                       if val 
is None:
+                                                                               
continue
+                                                                       
_log.error(u'PG diags %s: %s', prop, val)
                                                        if 
self.vars['ON_ERROR_STOP']:
-                                                               
_log.error(self.fmt_msg(error))
+                                                               
self.conn.commit()
+                                                               curs.close()
                                                                return 1
-                                                       else:
-                                                               
_log.debug(self.fmt_msg(error))
 
                                        self.conn.commit()
                                        curs.close()
@@ -204,6 +191,7 @@ class Psql:
                self.conn.commit()
                curs.close()
                return 0
+
 #===================================================================
 # testing code
 if __name__ == '__main__':
@@ -219,4 +207,3 @@ if __name__ == '__main__':
        psql = Psql (conn)
        psql.run (sys.argv[1])
        conn.close ()
-#===================================================================
diff --git a/server/pycommon/x-test-default_ro.py 
b/server/pycommon/x-test-default_ro.py
new file mode 100644
index 0000000..ef3df0c
--- /dev/null
+++ b/server/pycommon/x-test-default_ro.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+#
+# please run this script against a database which is configured to be readonly 
by:
+#
+#      alter databaes <NAME> set default_transaction_read_only to on
+#
+# if cmd line argument is "show_problem" -> exhibit the problem
+
+db = u'gnumed_v20'             # a database configured "alter database %s set 
default_transaction_read_only to on"
+user = 'gm-dbo'                        # a user with CREATE DATABASE powers
+
+#--------------------------------------------------------------------------------
+import sys
+import psycopg2
+
+
+cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings 
WHERE name = 'default_transaction_read_only'"
+cmd_create_db = "create database %s_copy template %s" % (db, db)
+cmd_drop_db = "drop database %s_copy" % db
+
+show_problem = False
+if len(sys.argv) > 1:
+       if sys.argv[1] == 'show_problem':
+               show_problem = True
+
+conn = psycopg2.connect(dbname = db, user = user)
+print 'conn:', conn
+print 'readonly:', conn.readonly
+print 'autocommit:', conn.autocommit
+print 'setting autocommit to False'
+conn.autocommit = False
+print 'autocommit now:', conn.autocommit
+if show_problem:
+       print 'vvvvv this creates the problem vvvvv'
+       print ' setting readonly to False'
+       conn.readonly = False
+       print ' readonly now:', conn.readonly
+       print '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^'
+print 'setting autocommit to True'
+conn.autocommit = True
+print 'autocommit now:', conn.autocommit
+print 'setting readonly to False'
+conn.readonly = False
+print 'readonly now:', conn.readonly
+curs = conn.cursor()
+curs.execute(cmd_def_tx_ro)
+print 'querying DEFAULT_TRANSACTION_READ_ONLY state (should show "ON")'
+print curs.fetchall()
+curs.close()
+conn.commit()
+print 'the following SQL will fail:', cmd_create_db
+print '(note that the transaction being talked about is implicit to 
PostgreSQL, due to autocommit mode)'
+curs = conn.cursor()
+try:
+       curs.execute(cmd_create_db)
+       curs.execute(cmd_drop_db)
+except psycopg2.InternalError as ex:
+       print 'SQL failed:'
+       print ex
+
+print 'shutting down'
+
+curs.close()
+conn.rollback()
+conn.close()
diff --git a/server/pycommon/x-test-psy.py b/server/pycommon/x-test-psy.py
new file mode 100644
index 0000000..f030df2
--- /dev/null
+++ b/server/pycommon/x-test-psy.py
@@ -0,0 +1,203 @@
+
+db = u'gnumed_v20'             # a database configured "alter database %s set 
default_transaction_read_only to on"
+user = 'gm-dbo'                        # a user with CREATE DATABASE powers
+
+
+
+cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings 
WHERE name = 'default_transaction_read_only'"
+cmd_create_db = "create database %s_copy template %s" % (db, db)
+
+
+import sys
+import psycopg2
+
+
+conn = psycopg2.connect(dbname = db, user = user)
+print 'readonly:', conn.readonly
+print 'autocommit:', conn.autocommit
+conn.readonly = False
+print 'readonly now:', conn.readonly
+#curs = conn.cursor()
+#curs.execute(cmd_def_tx_ro)
+#print 'should show DEFAULT_TRANSACTION_READ_ONLY set to ON'
+#print curs.fetchall()
+#curs.close()
+#conn.commit()
+conn.autocommit = True
+print 'readonly:', conn.readonly
+print 'autocommit:', conn.autocommit
+print 'the following CREATE DATABASE should fail'
+curs = conn.cursor()
+curs.execute(cmd_create_db)
+curs.close()
+conn.rollback()
+conn.close()
+
+sys.exit()
+
+
+
+
+
+curs = conn.cursor()
+#cmd_def_tx_ro = u'show default_transaction_read_only;'
+cmd_def_tx_ro = "SELECT upper(source), name, upper(setting) FROM pg_settings 
WHERE name = 'default_transaction_read_only'"
+cmd_tx_ro = u'show transaction_read_only;'
+cmd_DEL = u'DELETE FROM dem.identity where pk is NULL'
+
+print conn
+print 'initial RO state:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+
+#print ''
+print 'setting <conn.readonly = False> ...'
+conn.readonly = False
+print 'RO state in same TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+print 'RO state in next TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+
+print ''
+print 'setting <conn.autocommit = True> (conn.readonly still False) ...'
+print '-> means exiting psyco TX handling, needed for some DDL such as CREATE 
DATABASE ...'
+conn.autocommit = True
+print 'RO state in same TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+print 'RO state in next TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+
+print ''
+print 'setting <conn.autocommit = False> (conn.readonly still False) ...'
+print '-> means exiting psyco TX handling, needed for some DDL such as CREATE 
DATABASE ...'
+conn.autocommit = False
+print 'RO state in same TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+print 'RO state in same TX:'
+print '  psyco (conn.readonly):', conn.readonly
+print '  psyco (conn.autocommit):', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_def_tx_ro
+curs.execute(cmd_tx_ro)
+print '  PG:', curs.fetchall(), u'- %s' % cmd_tx_ro
+print '  running DELETE:', cmd_DEL
+try:
+       curs.execute(cmd_DEL)
+       print '  success'
+except Exception as e:
+       print '  failed:', e
+conn.commit()
+
+
+
+sys.exit()
+
+
+
+
+
+
+print 'RO state in same TX:'
+print '  psyco - conn.readonly:', conn.readonly
+print '  psyco - conn.autocommit:', conn.autocommit
+curs.execute(cmd_def_tx_ro)
+print '  PG - default_transaction_read_only:', curs.fetchall()
+curs.execute(cmd_tx_ro)
+print '  PG - transaction_read_only:', curs.fetchall()
+conn.commit()
+print 'RO state in next TX:'
+print '  psyco - conn.readonly:', conn.readonly
+curs.execute(cmd_def_tx_ro)
+print '  PG - default_transaction_read_only:', curs.fetchall()
+curs.execute(cmd_tx_ro)
+print '  PG - transaction_read_only:', curs.fetchall()
+conn.commit()
+
+print ''
+
+print 'PG/psyco split brain because of:'
+cmd = "SELECT upper(source), name, upper(setting) FROM pg_settings WHERE name 
= 'default_transaction_read_only'"
+print '  SQL:', cmd
+curs.execute(cmd)
+print '  PG:', curs.fetchall()
+
+
+
+conn.commit()
+curs.execute(u'DELETE FROM dem.identity where pk is NULL')
+
+
+curs.close()
+conn.commit()
+conn.close()
diff --git a/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql 
b/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql
index cce6ee1..253a1d8 100644
--- a/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql
+++ b/server/sql/v20-v21/dynamic/v21-release_notes-dynamic.sql
@@ -17,26 +17,24 @@ INSERT INTO dem.message_inbox (
 ) VALUES (
        (select pk from dem.staff where db_user = 'any-doc'),
        (select pk_type from dem.v_inbox_item_type where type = 'memo' and 
category = 'administrative'),
-       'Release Notes for GNUmed 1.6.13 (database v21.13)',
-       'GNUmed 1.6.13 Release Notes:
+       'Release Notes for GNUmed 1.6.14 (database v21.14)',
+       'GNUmed 1.6.14 Release Notes:
 
-       1.6.13
+       1.6.14
 
-FIX: editing of drug products
-FIX: formatting of intervals with seconds [thanks Rickard]
-FIX: robustify backend listener against change notification trigger errors
-FIX: backport once-only detection of unicode char selector
-FIX: improper handling of notebook page change events
-FIX: error handling on uploading DICOM to Orthanc
+FIX: exception when having issues with calculating eGFR in medication plugin
+FIX: exception on disabling identity [thanks Marc]
+FIX: exception on adding archived documents to export area
+FIX: Orthanc DICOM patient ID modification
+FIX: faulty file drop target declarations
 
-IMPROVED: more fully prevent logfile based password leaks
-IMPROVED: add listing of latest vaccination per indication
-IMPROVED: export area change listening and sortability
-IMPROVED: episode edit area behaviour
-IMPROVED: add measurement by clicking empty cell in grid
-
-NEW: add Constans algorithm for upper extremity DVT
+IMPROVED: saving of export area items
+IMPROVED: patient display in provider inbox
+IMPROVED: copy document to export area from document plugin
+IMPROVED: Orthanc modification dialog title
+IMPROVED: imported documents deletion confirmation
+IMPROVED: patient media metadata
 ');
 
 -- --------------------------------------------------------------
-select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.13');
+select gm.log_script_insertion('v21-release_notes-dynamic.sql', '21.14');

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/debian-med/gnumed-server.git

_______________________________________________
debian-med-commit mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit

Reply via email to