Repository: ckan/ckan

Issue:
Datastore _table_metadata view recreated on every startup
We fixed the read/write test having conflicting table names by randomizing them (https://github.com/ckan/ckan/pull/1397, https://github.com/ckan/ckan/issues/2082) but there's still another database call that can cause problems on busy sites: creating the _table_metadata view.
https://github.com/ckan/ckan/blob/b1c12f74b09c4703407563861e089e37ec0e19e2/ckanext/datastore/plugin.py#L80

My understanding of PostgreSQL views was once it's created, they update automatically without any further interaction. And if it's being recreated, all read queries against that view are locked until it is done?

Does this view really need to be created and/or recreated every time the plugin is loaded? 
Could it be done once on installation (either with documentation for a paster command or checking that it does not exist/is not the right structure before proceeding)?



Discussion:
@maxious You're right, this doesn't seem to make sense.

Should we introduce migrations for the datastore database to avoid this "live migration" thing we seem to be doing now?  Maybe start a directory under the datastore extension.


Resolving patch:
diff --git a/ckanext/datastore/plugin.py b/ckanext/datastore/plugin.py
--- a/ckanext/datastore/plugin.py
+++ b/ckanext/datastore/plugin.py
@@ -120,7 +120,6 @@ def configure(self, config):
                      'of _table_metadata are skipped.')
         else:
             self._check_urls_and_permissions()
-            self._create_alias_table()
 
     def notify(self, entity, operation=None):
         if not isinstance(entity, model.Package) or self.legacy_mode:
@@ -216,34 +215,6 @@ def _read_connection_has_correct_privileges(self):
             write_connection.close()
         return True
 
-    def _create_alias_table(self):
-        mapping_sql = '''
-            SELECT DISTINCT
-                substr(md5(dependee.relname || COALESCE(dependent.relname, '')), 0, 17) AS "_id",
-                dependee.relname AS name,
-                dependee.oid AS oid,
-                dependent.relname AS alias_of
-                -- dependent.oid AS oid
-            FROM
-                pg_class AS dependee
-                LEFT OUTER JOIN pg_rewrite AS r ON r.ev_class = dependee.oid
-                LEFT OUTER JOIN pg_depend AS d ON d.objid = r.oid
-                LEFT OUTER JOIN pg_class AS dependent ON d.refobjid = dependent.oid
-            WHERE
-                (dependee.oid != dependent.oid OR dependent.oid IS NULL) AND
-                (dependee.relname IN (SELECT tablename FROM pg_catalog.pg_tables)
-                    OR dependee.relname IN (SELECT viewname FROM pg_catalog.pg_views)) AND
-                dependee.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname='public')
-            ORDER BY dependee.oid DESC;
-        '''
-        create_alias_table_sql = u'CREATE OR REPLACE VIEW "_table_metadata" AS {0}'.format(mapping_sql)
-        try:
-            connection = db._get_engine(
-                {'connection_url': self.write_url}).connect()
-            connection.execute(create_alias_table_sql)
-        finally:
-            connection.close()
-
     def get_actions(self):
         actions = {'datastore_create': action.datastore_create,
                    'datastore_upsert': action.datastore_upsert,


Test patch: