[med-svn] [Git][med-team/gnumed-server][upstream] New upstream version 22.28

Andreas Tille (@tille) gitlab at salsa.debian.org
Sun Jan 28 17:07:08 GMT 2024



Andreas Tille pushed to branch upstream at Debian Med / gnumed-server


Commits:
9a9ffb0f by Andreas Tille at 2024-01-28T18:04:24+01:00
New upstream version 22.28
- - - - -


10 changed files:

- server/bootstrap/fixup_db-v22.conf
- server/bootstrap/update_db-v21_v22.conf
- server/doc/schema/gnumed-entire_schema.html
- server/gm-backup_and_zip_database.service
- server/gm-backup_and_zip_database.timer
- server/pycommon/gmBackendListener.py
- server/pycommon/gmConnectionPool.py
- server/pycommon/gmPG2.py
- server/sql/v21-v22/fixups/v22-gm-concat_table_structure_v19_and_up-fixup.sql
- server/sql/v21-v22/fixups/v22-release_notes-fixup.sql


Changes:

=====================================
server/bootstrap/fixup_db-v22.conf
=====================================
@@ -27,6 +27,7 @@ database alias = gnumed_v22
 minimum postgresql version = 9.2
 schema base directory = ../sql/v21-v22/fixups/
 schema = $schema$
+v22-gm-concat_table_structure_v19_and_up-fixup.sql
 v22-ref-paperwork_templates-fixups.sql
 v22-invoice_id_grants-fixup.sql
 v22-i18n-lang_funcs-fixup.sql
@@ -34,7 +35,6 @@ v22-clin-v_candidate_diagnoses-fixup.sql
 v22-dem-v_message_inbox-fixup.sql
 v22-add_generic_covid_vaccine.sql
 v22-clin-remove_old_empty_encounters-fixup.sql
-v22-gm-concat_table_structure_v19_and_up-fixup.sql
 v22-clin-move_waiting_list_entry-fixup.sql
 v22-blobs-doc_med-idx.sql
 v22-blobs-doc_obj-idx.sql
@@ -65,6 +65,7 @@ schema = $schema$
 $schema$
 
 superuser schema = $superuser schema$
+fixups/v22-gm-concat_table_structure_v19_and_up-fixup.sql
 superuser/v22-gm-load_auto_explain.sql
 $superuser schema$
 


=====================================
server/bootstrap/update_db-v21_v22.conf
=====================================
@@ -159,6 +159,7 @@ database alias = gnumed_v22
 minimum postgresql version = 9.2
 schema base directory = ../sql/v21-v22/fixups/
 schema = $schema$
+v22-gm-concat_table_structure_v19_and_up-fixup.sql
 v22-ref-paperwork_templates-fixups.sql
 v22-invoice_id_grants-fixup.sql
 v22-i18n-lang_funcs-fixup.sql
@@ -166,7 +167,6 @@ v22-clin-v_candidate_diagnoses-fixup.sql
 v22-dem-v_message_inbox-fixup.sql
 v22-add_generic_covid_vaccine.sql
 v22-clin-remove_old_empty_encounters-fixup.sql
-v22-gm-concat_table_structure_v19_and_up-fixup.sql
 v22-clin-move_waiting_list_entry-fixup.sql
 v22-blobs-doc_med-idx.sql
 v22-blobs-doc_obj-idx.sql
@@ -206,6 +206,7 @@ schema = $schema$
 $schema$
 
 superuser schema = $superuser schema$
+fixups/v22-gm-concat_table_structure_v19_and_up-fixup.sql
 superuser/v22-gm-load_auto_explain.sql
 $superuser schema$
 


=====================================
server/doc/schema/gnumed-entire_schema.html
=====================================
@@ -112,7 +112,7 @@
 <body>
 
 <!-- Primary Index -->
-<p><br><br>Dumped on 2023-12-25</p>
+<p><br><br>Dumped on 2024-01-28</p>
 <h1><a name="index">Index of database - gnumed_v22</a></h1>
 <ul>
   
@@ -158,7 +158,7 @@
   
   <li><a name="gm.schema">gm</a></li><ul>
     <li><a href="gnumed-entire_schema.html#gm.table.access-log">access_log</a></li><li><a href="gnumed-entire_schema.html#gm.table.notifying-tables">notifying_tables</a></li><li><a href="gnumed-entire_schema.html#gm.table.schema-revision">schema_revision</a></li>
-    <li><a href="gnumed-entire_schema.html#gm.function.account-is-dbowner-or-staff-account-name">account_is_dbowner_or_staff(_account name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-missing-array-bits">add_missing_array_bits()</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-table-for-notifies-name-name">add_table_for_notifies(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-user-to-permission-group-name-name">add_user_to_permission_group(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.array-unnest-anyarray">array_unnest(anyarray)</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure">concat_table_structure()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-integer">concat_table_structure(integer)</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v1">concat_table_structure_v1()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v16-and-up">concat_table_structure_v16_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v17-and-up">concat_table_structure_v17_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v18-and-up">concat_table_structure_v18_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v19-and-up">concat_table_structure_v19_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v2">concat_table_structure_v2()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v3">concat_table_structure_v3()</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-all-enc-epi-sanity-check-triggers">create_all_enc_epi_sanity_check_triggers()</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-all-table-mod-triggers-drop-old-triggers-boolean">create_all_table_mod_triggers(_drop_old_triggers boolean)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-enc-epi-sanity-check-trigger-schema-name-name-table-name-name-fk-encounter-col-name-fk-episode-col-name">create_enc_epi_sanity_check_trigger(_schema_name name, _table_name name, _fk_encounter_col name, _fk_episode_col name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-generic-combi-vaccine-text-textARRAY-text-boolean">create_generic_combi_vaccine(text, text[], text, boolean)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-table-mod-triggers-schema-name-name-table-name-name">create_table_mod_triggers(_schema_name name, _table_name name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-user-name-text">create_user(name, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.disable-user-name">disable_user(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.drop-user-name">drop_user(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.get-users">get_users()</a></li><li><a href="gnumed-entire_schema.html#gm.function.get-users-name">get_users(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.is-null-or-blank-string-text">is_null_or_blank_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.is-null-or-non-empty-string-text">is_null_or_non_empty_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.lo-chunked-md5-oid-integer">lo_chunked_md5(oid, integer)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-access2emr-text">log_access2emr(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-other-access-text">log_other_access(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-script-insertion-text-text">log_script_insertion(text, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.nullify-empty-string-text">nullify_empty_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.register-notifying-table-name-name">register_notifying_table(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.strip-allzeros-fraction-numeric">strip_allzeros_fraction(numeric)</a></li><li><a href="gnumed-entire_schema.html#gm.function.transfer-users-text">transfer_users(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.transfer-users-text-text">transfer_users(text, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.trf-announce-table-del">trf_announce_table_del()</a></li><li><a href="gnumed-entire_schema.html#gm.function.trf-announce-table-ins-upd">trf_announce_table_ins_upd()</a></li><li><a href="gnumed-entire_schema.html#gm.function.user-exists-name">user_exists(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.xid2int-xid">xid2int(xid)</a></li>
+    <li><a href="gnumed-entire_schema.html#gm.function.account-is-dbowner-or-staff-account-name">account_is_dbowner_or_staff(_account name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-missing-array-bits">add_missing_array_bits()</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-table-for-notifies-name-name">add_table_for_notifies(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.add-user-to-permission-group-name-name">add_user_to_permission_group(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.array-unnest-anyarray">array_unnest(anyarray)</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure">concat_table_structure()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-integer">concat_table_structure(integer)</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v1">concat_table_structure_v1()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v16-and-up">concat_table_structure_v16_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v17-and-up">concat_table_structure_v17_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v18-and-up">concat_table_structure_v18_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v19-and-up">concat_table_structure_v19_and_up()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v2">concat_table_structure_v2()</a></li><li><a href="gnumed-entire_schema.html#gm.function.concat-table-structure-v3">concat_table_structure_v3()</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-all-enc-epi-sanity-check-triggers">create_all_enc_epi_sanity_check_triggers()</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-all-table-mod-triggers-drop-old-triggers-boolean">create_all_table_mod_triggers(_drop_old_triggers boolean)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-enc-epi-sanity-check-trigger-schema-name-name-table-name-name-fk-encounter-col-name-fk-episode-col-name">create_enc_epi_sanity_check_trigger(_schema_name name, _table_name name, _fk_encounter_col name, _fk_episode_col name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-generic-combi-vaccine-text-textARRAY-text-boolean">create_generic_combi_vaccine(text, text[], text, boolean)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-table-mod-triggers-schema-name-name-table-name-name">create_table_mod_triggers(_schema_name name, _table_name name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.create-user-name-text">create_user(name, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.disable-user-name">disable_user(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.drop-user-name">drop_user(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.get-users">get_users()</a></li><li><a href="gnumed-entire_schema.html#gm.function.get-users-name">get_users(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.is-null-or-blank-string-text">is_null_or_blank_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.is-null-or-non-empty-string-text">is_null_or_non_empty_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.lo-chunked-md5-oid-integer">lo_chunked_md5(oid, integer)</a></li><li><a href="gnumed-entire_schema.html#gm.function.load-auto-explain-min-duration-integer">load_auto_explain(_min_duration integer)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-access2emr-text">log_access2emr(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-other-access-text">log_other_access(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.log-script-insertion-text-text">log_script_insertion(text, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.nullify-empty-string-text">nullify_empty_string(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.register-notifying-table-name-name">register_notifying_table(name, name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.strip-allzeros-fraction-numeric">strip_allzeros_fraction(numeric)</a></li><li><a href="gnumed-entire_schema.html#gm.function.transfer-users-text">transfer_users(text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.transfer-users-text-text">transfer_users(text, text)</a></li><li><a href="gnumed-entire_schema.html#gm.function.trf-announce-table-del">trf_announce_table_del()</a></li><li><a href="gnumed-entire_schema.html#gm.function.trf-announce-table-ins-upd">trf_announce_table_ins_upd()</a></li><li><a href="gnumed-entire_schema.html#gm.function.user-exists-name">user_exists(name)</a></li><li><a href="gnumed-entire_schema.html#gm.function.xid2int-xid">xid2int(xid)</a></li>
   </ul>
   
   <li><a name="i18n.schema">i18n</a></li><ul>
@@ -129829,6 +129829,29 @@ BEGIN
 	return md5(_md5_concat);
 END;</pre>
 
+<hr>
+<h2>Function:
+  <a href="gnumed-entire_schema.html#gm.schema">gm</a>.<a name="gm.function.load-auto-explain-min-duration-integer">load_auto_explain(_min_duration integer)</a>
+</h2>
+<h3>Returns: boolean</h3>
+<h3>Language: PLPGSQL</h3>
+<p>Load and configure auto_explain.
+.
+Integer argument = auto_explain.log_min_duration in milliseconds with a lower bound of 1500.</p>
+<pre>
+BEGIN
+	IF _min_duration < 1500 THEN
+		_min_duration := 1500;
+	END IF;
+	LOAD 'auto_explain';
+	PERFORM set_config('auto_explain.log_min_duration'::TEXT, _min_duration::TEXT, false);
+	PERFORM set_config('auto_explain.log_analyze'::TEXT, 'ON'::TEXT, false);
+	PERFORM set_config('auto_explain.log_verbose'::TEXT, 'ON'::TEXT, false);
+	PERFORM set_config('auto_explain.log_timing'::TEXT, 'ON'::TEXT, false);
+	PERFORM set_config('auto_explain.log_nested_statements'::TEXT, 'ON'::TEXT, false);
+	RETURN TRUE;
+END;</pre>
+
 <hr>
 <h2>Function:
   <a href="gnumed-entire_schema.html#gm.schema">gm</a>.<a name="gm.function.log-access2emr-text">log_access2emr(text)</a>


=====================================
server/gm-backup_and_zip_database.service
=====================================
@@ -1,5 +1,7 @@
+# put into /usr/lib/systemd/system/
 [Unit]
 Description="GNUmed database backup service"
+Documentation=https://www.gnumed.de/documentation/
 Requires=gm-backup_and_zip_database.timer
 
 [Service]


=====================================
server/gm-backup_and_zip_database.timer
=====================================
@@ -1,3 +1,4 @@
+# put into /usr/lib/systemd/system/
 [Unit]
 Description="GNUmed database backup timer"
 


=====================================
server/pycommon/gmBackendListener.py
=====================================
@@ -35,13 +35,9 @@ class gmBackendListener(gmBorg.cBorg):
 
 	def __init__(self, conn=None, poll_interval:int=3):
 
-		try:
-			self.already_inited
+		if hasattr(self, 'already_inited'):
 			return
 
-		except AttributeError:
-			pass
-
 		#gmLog2.log_step(restart = True)
 		assert conn, '<conn> must be given'
 
@@ -429,6 +425,8 @@ if __name__ == "__main__":
 		print("shutting down backend notifications monitor")
 
 	#-------------------------------
+	gmPG2.request_login_params(setup_pool = True)
+
 	if sys.argv[1] == 'monitor':
 		run_monitor()
 	else:


=====================================
server/pycommon/gmConnectionPool.py
=====================================
@@ -706,6 +706,7 @@ def log_pg_exception_details(exc):
 	if not isinstance(exc, dbapi.Error):
 		return False
 
+	_log.error(type(exc))
 	try:
 		args = exc.args
 		for arg in args:


=====================================
server/pycommon/gmPG2.py
=====================================
@@ -55,9 +55,8 @@ except ImportError:
 
 import psycopg2.errorcodes as sql_error_codes
 import psycopg2.sql as psysql
-#import psycopg2.errors as PG_EXCEPTIONS
 
-PG_ERROR_EXCEPTION = dbapi.Error
+PG_ERROR_EXCEPTION = dbapi.DatabaseError
 
 # =======================================================================
 default_database = 'gnumed_v22'
@@ -374,8 +373,8 @@ def request_login_params(setup_pool=False):
 # =======================================================================
 # netadata API
 # =======================================================================
-SQL__concat_table_structure_v19_and_up = """
-create or replace function gm.concat_table_structure_v19_and_up()
+SQL__pg_temp_concat_table_structure_v19_and_up = """
+create or replace function pg_temp.concat_table_structure_v19_and_up()
 	returns text
 	language 'plpgsql'
 	security definer
@@ -388,7 +387,6 @@ declare
 	_total text;
 begin
 	_total := '''';
-
 	-- find relevant tables
 	for _table_desc in
 		select * from information_schema.tables tabs where
@@ -397,12 +395,10 @@ begin
 			tabs.table_type = ''BASE TABLE''
 		order by
 			decode(md5(tabs.table_schema || tabs.table_name), ''hex'')
-
 	-- loop over tables
 	loop
 		-- where are we at ?
-		_total := _total || ''TABLE:'' || _table_desc.table_schema || ''.'' || _table_desc.table_name || E''\\n'';
-
+		_total := _total || ''TABLE:'' || _table_desc.table_schema || ''.'' || _table_desc.table_name || E''\n'';
 		-- find PKs of that table
 		for _pk_desc in
 			select * from (
@@ -426,7 +422,7 @@ begin
 				decode(md5(PKs.primary_key_column), ''hex'')
 		-- and loop over those PK columns
 		loop
-			_total := _total || ''PK:'' || _pk_desc.primary_key_column	|| E''\\n'';
+			_total := _total || ''PK:'' || _pk_desc.primary_key_column	|| E''\n'';
 		end loop;
 
 		-- find columns of that table
@@ -446,10 +442,9 @@ begin
 				|| _column_desc.table_schema || ''.''
 				|| _column_desc.table_name || ''.''
 				|| _column_desc.column_name || ''::''
-				|| _column_desc.udt_name || E''\\n'';
+				|| _column_desc.udt_name || E''\n'';
 
 		end loop;
-
 		-- find and loop over CONSTRAINTs of that table
 		for _constraint_def in
 			select * from
@@ -476,69 +471,158 @@ begin
 				CONSTRAINTs.contype,
 				decode(md5(CONSTRAINTs.condef), ''hex'')
 		loop
-			_total := _total || _constraint_def.condef || E''\\n'';
+			_total := _total || _constraint_def.condef || E''\n'';
 		end loop;
-
 	end loop;		-- over tables
-
 	return _total;
 end;';
-
-select md5(gm.concat_table_structure(%(ver)s::integer)) AS md5;
 """
+SQL__get_pg_temp_table_structure = "select pg_temp.concat_table_structure_v19_and_up();"
+SQL__md5_pg_temp_table_structure = "select md5(pg_temp.concat_table_structure_v19_and_up()) AS md5;"
 
+#------------------------------------------------------------------------
 def database_schema_compatible(link_obj=None, version=None, verbose=True):
 	expected_hash = known_schema_hashes[version]
-	if version == 0:
-		args = {'ver': 9999}
-	else:
-		args = {'ver': version}
-	SQL = 'select md5(gm.concat_table_structure(%(ver)s::integer)) as md5'
-	try:
-		rows, idx = run_ro_queries(link_obj = link_obj, queries = [{'cmd': SQL, 'args': args}])
-	except dbapi.errors.AmbiguousFunction as exc:
-		gmConnectionPool.log_pg_exception_details(exc)
-		if not hasattr(exc, 'diag'):
-			raise
-		if 'gm.concat_table_structure_v19_and_up()' not in exc.diag.context:
-			raise
-		rows = None
-	if rows is None:
-		_log.error('gm.concat_table_structure_v19_and_up() failed, retrying with updated function')
-		rows, idx = run_ro_queries(link_obj = link_obj, queries = [{'cmd': SQL__concat_table_structure_v19_and_up, 'args': args}])
-	if rows[0]['md5'] == expected_hash:
-		_log.info('detected schema version [%s], hash [%s]' % (map_schema_hash2version[rows[0]['md5']], rows[0]['md5']))
+	ver = 9999 if version == 0 else version
+	md5_db = get_schema_hash(link_obj = link_obj, version = ver)
+	if md5_db == expected_hash:
+		_log.info('detected schema version [%s], hash [%s]' % (map_schema_hash2version[md5_db], md5_db))
 		return True
 
 	_log.error('database schema version mismatch')
 	_log.error('expected: %s (%s)' % (version, expected_hash))
-	_log.error('detected: %s (%s)' % (get_schema_version(link_obj=link_obj), rows[0]['md5']))
+	try:
+		_log.error('detected: %s (%s)', map_schema_hash2version[md5_db], md5_db)
+	except KeyError:
+		_log.error('detected: <unknown> (%s)', md5_db)
 	if verbose:
-		_log.debug('schema dump follows:')
-		for line in get_schema_structure(link_obj = link_obj).split():
-			_log.debug(line)
-		_log.debug('schema revision history dump follows:')
-		for line in get_schema_revision_history(link_obj = link_obj):
-			_log.debug(' - '.join(line))
+		log_schema_structure(link_obj = link_obj)
+		log_schema_revision_history(link_obj = link_obj)
 	return False
 
 #------------------------------------------------------------------------
 def get_schema_version(link_obj=None):
-	rows, idx = run_ro_queries(link_obj=link_obj, queries = [{'cmd': 'select md5(gm.concat_table_structure()) as md5'}])
+	md5_db = get_schema_hash(link_obj = link_obj)
+	if not md5_db:
+		_log.error('cannot determine schema version')
+		return None
+
 	try:
-		return map_schema_hash2version[rows[0]['md5']]
+		return map_schema_hash2version[md5_db]
+
 	except KeyError:
-		return 'unknown database schema version, MD5 hash is [%s]' % rows[0]['md5']
+		return 'unknown database schema version, MD5 hash is [%s]' % md5_db
+
+#------------------------------------------------------------------------
+def __get_schema_structure_by_gm_func(link_obj=None) -> str:
+	SQL = 'select gm.concat_table_structure()'
+	try:
+		rows, idx = run_ro_queries(link_obj=link_obj, queries = [{'cmd': SQL}])
+		return rows[0][0]
+
+	except dbapi.errors.AmbiguousFunction as exc:
+		if hasattr(exc, 'diag') and 'gm.concat_table_structure_v19_and_up()' in exc.diag.context:
+			_log.error('gm.concat_table_structure_v19_and_up() failed')
+			return None
+
+		gmConnectionPool.log_pg_exception_details(exc)
+		raise
+
+#------------------------------------------------------------------------
+def __get_schema_structure_by_pg_temp_func() -> str:
+	queries = [
+		{'cmd': SQL__pg_temp_concat_table_structure_v19_and_up},
+		{'cmd': SQL__get_pg_temp_table_structure}
+	]
+	conn = get_connection(readonly = False)
+	try:
+		rows, idx = run_rw_queries(link_obj = conn, queries = queries, return_data = True)
+		return rows[0][0]
+
+	except PG_ERROR_EXCEPTION as exc:
+		_log.error('pg_temp.concat_table_structure_v19_and_up() failed')
+		gmConnectionPool.log_pg_exception_details(exc)
+		raise
+
+	finally:
+		conn.rollback()
+		conn.close()
+
+	# should never get here
+	return None
 
 #------------------------------------------------------------------------
 def get_schema_structure(link_obj=None):
-	rows, idx = run_ro_queries(link_obj=link_obj, queries = [{'cmd': 'select gm.concat_table_structure()'}])
-	return rows[0][0]
+	schema_struct = __get_schema_structure_by_gm_func(link_obj = link_obj)
+	if not schema_struct:
+		_log.debug('retrying with temporary function')
+		schema_struct = __get_schema_structure_by_pg_temp_func()
+	return schema_struct
 
 #------------------------------------------------------------------------
-def get_schema_hash(link_obj=None):
-	rows, idx = run_ro_queries(link_obj=link_obj, queries = [{'cmd': 'select md5(gm.concat_table_structure()) as md5'}])
-	return rows[0]['md5']
+def log_schema_structure(link_obj=None):
+	_log.debug('schema structure dump:')
+	schema_struct = get_schema_structure(link_obj = link_obj)
+	if not schema_struct:
+		_log.error('cannot determine schema structure')
+		return
+
+	for line in schema_struct.split():
+		_log.debug(line)
+
+#------------------------------------------------------------------------
+def __get_schema_hash_by_gm_func(link_obj=None, version=None) -> str:
+	args = {}
+	if version:
+		SQL = 'SELECT md5(gm.concat_table_structure(%(ver)s::INTEGER)) AS md5'
+		args['ver'] = version
+	else:
+		SQL = 'SELECT md5(gm.concat_table_structure()) AS md5'
+	_log.debug('version: %s', version)
+	try:
+		rows, idx = run_ro_queries(link_obj=link_obj, queries = [{'cmd': SQL, 'args': args}])
+		_log.debug('hash: %s', rows[0]['md5'])
+		return rows[0]['md5']
+
+	except dbapi.errors.AmbiguousFunction as exc:
+		if hasattr(exc, 'diag') and 'gm.concat_table_structure_v19_and_up()' in exc.diag.context:
+			_log.error('gm.concat_table_structure_v19_and_up() failed')
+			return None
+
+		gmConnectionPool.log_pg_exception_details(exc)
+		raise
+
+#------------------------------------------------------------------------
+def __get_schema_hash_by_pg_temp_func() -> str:
+	conn = get_connection(readonly = False)
+	queries = [
+		{'cmd': SQL__pg_temp_concat_table_structure_v19_and_up},
+		{'cmd': SQL__md5_pg_temp_table_structure}
+	]
+	try:
+		rows, idx = run_rw_queries(link_obj = conn, queries = queries, return_data = True)
+		_log.debug('hash: %s', rows[0]['md5'])
+		return rows[0]['md5']
+
+	except PG_ERROR_EXCEPTION as exc:
+		_log.error('pg_temp.concat_table_structure_v19_and_up() failed')
+		gmConnectionPool.log_pg_exception_details(exc)
+		raise
+
+	finally:
+		conn.rollback()
+		conn.close()
+
+	# should never get here
+	return None
+
+#------------------------------------------------------------------------
+def get_schema_hash(link_obj=None, version=None):
+	md5_db = __get_schema_hash_by_gm_func(link_obj = link_obj)
+	if not md5_db:
+		_log.debug('retrying with temporary function')
+		md5_db = __get_schema_hash_by_pg_temp_func()
+	return md5_db
 
 #------------------------------------------------------------------------
 def get_schema_revision_history(link_obj=None):
@@ -565,6 +649,12 @@ def get_schema_revision_history(link_obj=None):
 	rows, idx = run_ro_queries(link_obj = link_obj, queries = [{'cmd': cmd}])
 	return rows
 
+#------------------------------------------------------------------------
+def log_schema_revision_history(link_obj=None):
+	_log.debug('schema revision history dump:')
+	for line in get_schema_revision_history(link_obj = link_obj):
+		_log.debug(' - '.join(line))
+
 #------------------------------------------------------------------------
 def get_db_fingerprint(conn=None, fname=None, with_dump=False, eol=None):
 	queries = [
@@ -600,10 +690,7 @@ def get_db_fingerprint(conn=None, fname=None, with_dump=False, eol=None):
 	rows = curs.fetchall()
 	lines.append('%20s: %s' % ('Size (DB)', rows[0][0]))
 	# get hash
-	cmd = "SELECT md5(gm.concat_table_structure())"
-	curs.execute(cmd)
-	rows = curs.fetchall()
-	md5_sum = rows[0][0]
+	md5_sum = get_schema_hash(link_obj = curs)
 	try:
 		lines.append('%20s: %s (v%s)' % ('Schema hash', md5_sum, map_schema_hash2version[md5_sum]))
 	except KeyError:
@@ -613,10 +700,8 @@ def get_db_fingerprint(conn=None, fname=None, with_dump=False, eol=None):
 		rows = curs.fetchall()
 		lines.append('%20s: %s' % (label, rows[0][0]))
 	if with_dump:
-		curs.execute('SELECT gm.concat_table_structure()')
-		rows = curs.fetchall()
 		lines.append('')
-		lines.append(rows[0][0])
+		lines.append(str(get_schema_structure(link_obj = curs)))
 	curs.close()
 	if fname is None:
 		if eol is None:
@@ -1755,7 +1840,7 @@ def run_ro_queries(link_obj=None, queries=None, verbose=False, return_data=True,
 				_log.exception('cannot rollback transaction')
 				gmConnectionPool.log_pg_exception_details(pg_exc2)
 			if pg_exc.pgcode == sql_error_codes.INSUFFICIENT_PRIVILEGE:
-				details = 'Query: [%s]' % curs.query.strip().strip('\n').strip().strip('\n')
+				details = 'Query: [%s]' % curs.query.decode(errors = 'replace').strip().strip('\n').strip().strip('\n')
 				if curs.statusmessage != '':
 					details = 'Status: %s\n%s' % (
 						curs.statusmessage.strip().strip('\n').strip().strip('\n'),
@@ -1911,7 +1996,7 @@ def run_rw_queries(link_obj=None, queries=None, end_tx=False, return_data=None,
 				gmConnectionPool.log_pg_exception_details(pg_exc2)
 			# privilege problem
 			if pg_exc.pgcode == sql_error_codes.INSUFFICIENT_PRIVILEGE:
-				details = 'Query: [%s]' % curs.query.strip().strip('\n').strip().strip('\n')
+				details = 'Query: [%s]' % curs.query.decode(errors = 'replace').strip().strip('\n').strip().strip('\n')
 				if curs.statusmessage != '':
 					details = 'Status: %s\n%s' % (
 						curs.statusmessage.strip().strip('\n').strip().strip('\n'),
@@ -2241,9 +2326,10 @@ if __name__ == "__main__":
 	if sys.argv[1] != 'test':
 		sys.exit()
 
-	from Gnumed.pycommon.gmTools import file2md5
+	#from Gnumed.pycommon.gmTools import file2md5
 
 	logging.basicConfig(level=logging.DEBUG)
+	gmLog2.print_logfile_name()
 
 	#--------------------------------------------------------------------
 	def test_file2bytea():
@@ -2731,6 +2817,37 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
 		#print(get_db_fingerprint(conn, with_dump = True, eol = '\n'))
 		print(get_db_fingerprint(with_dump = True, eol = '\n'))
 
+	#--------------------------------------------------------------------
+	def test_schema_compatible():
+		request_login_params(setup_pool = True)
+		print(database_schema_compatible(version=22, verbose=True))
+
+	#--------------------------------------------------------------------
+	def test_get_schema_structure():
+		request_login_params(setup_pool = True)
+		print(get_schema_structure())
+
+	#--------------------------------------------------------------------
+	def test_pg_temp_concat():
+		request_login_params(setup_pool = True)
+		conn = get_connection(readonly = False)
+		queries = [
+			{'cmd': SQL__pg_temp_concat_table_structure_v19_and_up},
+			{'cmd': SQL__get_pg_temp_table_structure}
+		]
+		rows, idx = run_rw_queries(link_obj = conn, queries = queries, return_data = True)
+		conn.rollback()
+		conn.close()
+		print(rows[0][0])
+
+	#--------------------------------------------------------------------
+	def test___get_schema_structure():
+		request_login_params(setup_pool = True)
+		with open('x-gm_func.txt', 'w', encoding = 'utf8') as f:
+			f.write(__get_schema_structure_by_gm_func())
+		with open('x-pg_temp_func.txt', 'w', encoding = 'utf8') as f:
+			f.write(__get_schema_structure_by_pg_temp_func())
+
 	#--------------------------------------------------------------------
 	# run tests
 
@@ -2749,7 +2866,7 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
 	#test_sanitize_pg_regex()
 	#test_is_pg_interval()
 	#test_sanity_check_time_skew()
-	test_sanity_check_database_settings()
+	#test_sanity_check_database_settings()
 	#test_get_foreign_key_details()
 	#test_get_index_name()
 	#test_set_user_language()
@@ -2761,5 +2878,9 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
 	#test_faulty_SQL()
 	#test_log_settings()
 	#test_get_db_fingerprint()
+	#test_schema_compatible()
+	#test_get_schema_structure()
+	test___get_schema_structure()
+	#test_pg_temp_concat()
 
 # ======================================================================


=====================================
server/sql/v21-v22/fixups/v22-gm-concat_table_structure_v19_and_up-fixup.sql
=====================================
@@ -11,6 +11,31 @@
 --set default_transaction_read_only to off;
 set check_function_bodies to on;
 
+-- --------------------------------------------------------------
+-- a few missing grants on (mostly unused) tables prevented
+-- accounts other than the owner (gm-dbo) from seeing them
+-- inside information_schema.tables which then throws off
+-- getting the database schema structure
+--
+-- now, this won't matter as long as gm.concat_table_structure()
+-- is used since that is owned by gm-dob _and_ also "security definer"
+--
+-- however, when using an ad-hoc temporary version in pg_temp to
+-- overcome other problems (such as missing CASTs) that version is
+-- owned and run by other accounts, which in turn won't see some
+-- rows in information_schema, making the function return a different
+-- table structure than is expected (and in the database)
+grant select, insert, update, delete on
+	clin.incoming_data_unmatchable,
+	clin.lnk_substance2episode,
+	ref.atc_staging		-- no PK hence no PK sequence
+to group "gm-doctors";
+
+grant usage, select, update on
+	clin.incoming_data_unmatchable_pk_seq,
+	clin.lnk_substance2episode_pk_seq
+to group "gm-doctors";
+
 -- --------------------------------------------------------------
 create or replace function gm.concat_table_structure_v19_and_up()
 	returns text
@@ -122,6 +147,9 @@ begin
 end;
 ';
 
+ALTER function gm.concat_table_structure_v19_and_up()
+	owner to "gm-dbo";
+
 comment on function gm.concat_table_structure_v19_and_up() is
 	'new concat_table_structure() starting with gnumed_v19,
 	 works on dem, clin, blobs, cfg, ref, i18n, bill,
@@ -129,4 +157,4 @@ comment on function gm.concat_table_structure_v19_and_up() is
 	 sorts properly by bytea';
 
 -- ==============================================================
-select gm.log_script_insertion('v22-gm-concat_table_structure_v19_and_up-fixup.sql', '22.18');
+select gm.log_script_insertion('v22-gm-concat_table_structure_v19_and_up-fixup.sql', '22.28');


=====================================
server/sql/v21-v22/fixups/v22-release_notes-fixup.sql
=====================================
@@ -17,24 +17,21 @@ 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.8.17 (database v22.27)',
-	'GNUmed 1.8.17 Release Notes:
+	'Release Notes for GNUmed 1.8.18 (database v22.28)',
+	'GNUmed 1.8.18 Release Notes:
 
-	1.8.17
+	1.8.18
 
-FIX: patient search exception
-FIX: OOo startup exception
-FIX: placeholders today/date_of_birth/name parsing if no format given
-FIX: logging of invalid form templates
-FIX: logging of invalid address data
+IMPROVED: new-patient: dialog layout
+IMPROVED: phrasewheels: in-focus signalling
+IMPROVED: less diagnostic GTK output on console
 
-IMPROVED: EMR browser: link document review dlg from document nodes
-IMPROVED: configuration: logging of set-option failures
+	22.28
 
-	22.27
+FIX: concatenation of the database schema structure
 
-NEW: add systemd .timer/.service files for scheduling database backup
+IMPROVED: documentation for backup systemd .service file
 ');
 
 -- --------------------------------------------------------------
-select gm.log_script_insertion('v22-release_notes-fixup.sql', '22.27 at 1.8.17');
+select gm.log_script_insertion('v22-release_notes-fixup.sql', '22.28 at 1.8.18');



View it on GitLab: https://salsa.debian.org/med-team/gnumed-server/-/commit/9a9ffb0fd78b03d58b4484c5ff7771ada6cf8bb2

-- 
View it on GitLab: https://salsa.debian.org/med-team/gnumed-server/-/commit/9a9ffb0fd78b03d58b4484c5ff7771ada6cf8bb2
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20240128/5ac16f7e/attachment-0001.htm>


More information about the debian-med-commit mailing list