From 843010d7cf90ac36f43f412484be26ce24109988 Mon Sep 17 00:00:00 2001 From: Conrad Vermeulen Date: Fri, 14 Dec 2007 10:03:21 +0000 Subject: [PATCH] KTS-2743 "upgrade functionality needs to apply foreign key constrains." Implemented. --- bin/recreateIndexes.php | 781 ++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- lib/database/schema.inc.php | 1001 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/upgrades/UpgradeFunctions.inc.php | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1063 insertions(+), 772 deletions(-) create mode 100644 lib/database/schema.inc.php diff --git a/bin/recreateIndexes.php b/bin/recreateIndexes.php index 3602f71..34748d3 100644 --- a/bin/recreateIndexes.php +++ b/bin/recreateIndexes.php @@ -39,785 +39,24 @@ /* * PURPOSE: This script will recreate the indexes on the database. It will also attempt to add foreign key constraints. * - * NOTE: It was developed on mysql 5, so there may be a requirement for this to be running! - * NOTE: This assumes that the db is in the 3.5.0 state! - * * It will produce 'errors' when there are issues. Many may be ignored as some do not apply to open source. */ +define('USE_DB_ADMIN_USER',1); chdir(dirname(__FILE__)); require_once('../config/dmsDefaults.php'); +require_once(KT_LIB_DIR . '/database/schema.inc.php'); print _kt('Recreate DB Indexes') . "...\n\n"; -$recreator = new IndexRecreator(); -$recreator->globalStart(); - -do -{ - $dropped = $recreator->dropIndexes(); -} while ($dropped != 0); - -$recreator->applyPreFixes(); -$recreator->addPrimaryKeys(); -$recreator->addForeignKeys(); -$recreator->removeDuplicateIndexes(); -$recreator->addOtherIndexes(); -$recreator->applyPostFixes(); - -print sprintf(_kt('Total time: %s'), $recreator->globalEnd()); - -print _kt('Done.') . "\n"; -exit; - -class IndexRecreator -{ - var $knownKeys; - var $knownPrimary; - var $newPrimary; - var $newKeys; - var $exec; - var $debugSQL = false; - var $verbose = true; - - var $foreignkeys; - var $primary; - var $globalstart; - var $start; - var $tables; - - function microtimeFloat() - { - list($usec, $sec) = explode(" ", microtime()); - return ((float)$usec + (float)$sec); - } - - function globalStart() - { - $this->globalstart = $this->microtimeFloat(); - } - - function start() - { - $this->start = $this->microtimeFloat(); - } - - function globalEnd() - { - $time = $this->microtimeFloat() - $this->globalstart; - - return number_format($time,2,'.',',') . 's'; - } - - function end() - { - $time = $this->microtimeFloat() - $this->start; - return number_format($time,2,'.',',') . "s"; - } - - function IndexRecreator() - { - $this->knownKeys = array(); - $this->knownPrimary = array(); - $this->newPrimary = array(); - $this->newKeys = array(); - $this->exec = true; - } - - function applyPreFixes() - { - - } - - function addForeignKeys() - { - $this->addForeignKey('active_sessions', 'user_id', 'users', 'id'); - - $this->addForeignKey('archive_restoration_request', 'document_id', 'documents', 'id'); - $this->addForeignKey('archive_restoration_request', 'request_user_id', 'users', 'id'); - $this->addForeignKey('archive_restoration_request', 'admin_user_id', 'users', 'id'); - - $this->addForeignKey('archiving_settings', 'archiving_type_id', 'archiving_type_lookup', 'id'); - $this->addForeignKey('archiving_settings', 'time_period_id', 'time_period', 'id'); - - $this->addForeignKey('baobab_user_keys', 'user_id', 'users', 'id'); - $this->addForeignKey('baobab_user_keys', 'key_id', 'baobab_keys', 'id'); - - $this->addForeignKey('comment_searchable_text', 'comment_id', 'discussion_comments', 'id'); - $this->addForeignKey('comment_searchable_text', 'document_id', 'documents', 'id'); - - $this->addForeignKey('dashlet_disables', 'user_id', 'users', 'id'); - - $this->addForeignKey('discussion_comments', 'thread_id', 'discussion_threads', 'id'); - $this->addForeignKey('discussion_comments', 'user_id', 'users', 'id'); - $this->addForeignKey('discussion_comments', 'in_reply_to', 'discussion_comments', 'id'); - - $this->addForeignKey('discussion_threads', 'document_id', 'documents', 'id'); - $this->addForeignKey('discussion_threads', 'first_comment_id', 'discussion_comments', 'id'); - $this->addForeignKey('discussion_threads', 'last_comment_id', 'discussion_comments', 'id'); - $this->addForeignKey('discussion_threads', 'creator_id', 'users', 'id'); - - $this->addForeignKey('document_archiving_link', 'document_id', 'documents', 'id'); - $this->addForeignKey('document_archiving_link', 'archiving_settings_id', 'archiving_settings', 'id'); - - $this->addForeignKey('document_content_version', 'document_id', 'documents', 'id'); - $this->addForeignKey('document_content_version', 'mime_id', 'mime_types', 'id'); - - $this->addForeignKey('document_fields','parent_fieldset','fieldsets','id'); - - $this->addForeignKey('document_fields_link','document_field_id','document_fields','id'); - $this->addForeignKey('document_fields_link','metadata_version_id','document_metadata_version','id'); - - $this->addForeignKey('document_link','parent_document_id', 'documents', 'id'); - $this->addForeignKey('document_link','child_document_id', 'documents', 'id'); - $this->addForeignKey('document_link','link_type_id','document_link_types','id'); - - $this->addForeignKey('document_metadata_version','document_type_id','document_types_lookup','id'); - $this->addForeignKey('document_metadata_version','status_id','status_lookup','id'); - $this->addForeignKey('document_metadata_version','document_id','documents','id'); - $this->addForeignKey('document_metadata_version','version_creator_id','users','id'); - $this->addForeignKey('document_metadata_version','content_version_id','document_content_version','id'); - $this->addForeignKey('document_metadata_version','workflow_id','workflows','id'); - $this->addForeignKey('document_metadata_version','workflow_state_id','workflow_states','id'); - - $this->addForeignKey('document_role_allocations','role_id','roles','id'); - $this->addForeignKey('document_role_allocations','permission_descriptor_id','permission_descriptors','id'); - - $this->addForeignKey('document_searchable_text','document_id','documents','id'); - - $this->addForeignKey('document_subscriptions','user_id','users','id'); - $this->addForeignKey('document_subscriptions','document_id','documents','id'); - - $this->addForeignKey('document_tags','document_id','documents','id'); - $this->addForeignKey('document_tags','tag_id','tag_words','id'); - - $this->addForeignKey('document_text','document_id','documents','id'); - - - $this->addForeignKey('document_transaction_text','document_id','documents','id'); - - $this->addForeignKey('document_type_fields_link','document_type_id', 'document_types_lookup','id'); - $this->addForeignKey('document_type_fields_link','field_id','document_fields','id'); - - $this->addForeignKey('document_type_fieldsets_link','document_type_id', 'document_types_lookup','id'); - $this->addForeignKey('document_type_fieldsets_link','fieldset_id','fieldsets','id'); - - $this->addForeignKey('documents','creator_id','users','id', 'SET NULL', 'SET NULL'); - $this->addForeignKey('documents','folder_id','folders','id'); // we don't want this - $this->addForeignKey('documents','checked_out_user_id','users','id', 'SET NULL', 'SET NULL'); - $this->addForeignKey('documents','status_id','status_lookup','id'); - $this->addForeignKey('documents','permission_object_id','permission_objects','id'); - $this->addForeignKey('documents','permission_lookup_id','permission_lookups','id'); - $this->addForeignKey('documents','modified_user_id','users','id', 'SET NULL', 'SET NULL'); - $this->addForeignKey('documents','metadata_version_id','document_metadata_version','id'); - - $this->addForeignKey('download_files','document_id','documents','id'); - - $this->addForeignKey('field_behaviour_options','behaviour_id','field_behaviours','id'); - $this->addForeignKey('field_behaviour_options','field_id','document_fields','id'); - $this->addForeignKey('field_behaviour_options','instance_id','field_value_instances','id'); - - $this->addForeignKey('field_behaviours','field_id','document_fields','id'); - - $this->addForeignKey('field_orders','child_field_id','document_fields','id'); - $this->addForeignKey('field_orders','parent_field_id','document_fields','id'); - $this->addForeignKey('field_orders','fieldset_id','fieldsets','id'); - - $this->addForeignKey('field_value_instances','field_value_id','metadata_lookup','id'); // it is so.. strange ;) - $this->addForeignKey('field_value_instances','behaviour_id','field_behaviours','id'); - $this->addForeignKey('field_value_instances','field_id','document_fields','id'); - - $this->addForeignKey('fieldsets','master_field','document_fields','id'); - - $this->addForeignKey('folder_descendants','parent_id','folders','id'); - $this->addForeignKey('folder_descendants','folder_id','folders','id'); - - $this->addForeignKey('folder_doctypes_link','folder_id','folders','id'); - $this->addForeignKey('folder_doctypes_link','document_type_id','document_types_lookup','id'); - - $this->addForeignKey('folder_searchable_text','folder_id','folders','id'); - - $this->addForeignKey('folder_subscriptions','user_id','users','id'); - $this->addForeignKey('folder_subscriptions','folder_id','folders','id'); - - $this->addForeignKey('folder_workflow_map','folder_id', 'folders','id'); - $this->addForeignKey('folder_workflow_map','workflow_id', 'workflows','id'); - - $this->addForeignKey('folders','creator_id','users','id'); - $this->addForeignKey('folders','permission_object_id','permission_objects','id'); - $this->addForeignKey('folders','permission_lookup_id','permission_lookups','id'); - $this->addForeignKey('folders','parent_id','folders','id'); - - $this->addForeignKey('folders_users_roles_link','user_id','users','id'); - $this->addForeignKey('folders_users_roles_link','document_id','documents','id'); - - $this->addForeignKey('groups_groups_link','parent_group_id','groups_lookup','id'); - $this->addForeignKey('groups_groups_link','member_group_id','groups_lookup','id'); - - $this->addForeignKey('groups_lookup','unit_id', 'units_lookup','id'); - - $this->addForeignKey('index_files','document_id','documents','id'); - $this->addForeignKey('index_files','user_id','users','id'); - - $this->addForeignKey('metadata_lookup','document_field_id','document_fields','id'); -// $this->addForeignKey('metadata_lookup','treeorg_parent','??','id'); - - $this->addForeignKey('metadata_lookup_tree','document_field_id', 'document_fields','id'); -// $this->addForeignKey('metadata_lookup_tree','metadata_lookup_tree_parent', '??','id'); - - $this->addForeignKey('mime_types','mime_document_id','mime_documents','id', 'set null', 'set null'); - $this->addForeignKey('mime_types','extractor_id','mime_extractors','id', 'set null', 'set null'); - - $this->addForeignKey('mime_document_mapping','mime_type_id','mime_types','id'); - $this->addForeignKey('mime_document_mapping','mime_document_id','mime_documents','id'); - - $this->addForeignKey('news','image_mime_type_id','mime_types','id'); - - $this->addForeignKey('notifications','user_id', 'users','id'); - - $this->addForeignKey('permission_assignments','permission_id', 'permissions','id'); - $this->addForeignKey('permission_assignments','permission_object_id','permission_objects','id'); // duplicate - $this->addForeignKey('permission_assignments','permission_descriptor_id','permission_descriptors','id'); - - $this->addForeignKey('permission_descriptor_groups','descriptor_id','permission_descriptors','id'); - $this->addForeignKey('permission_descriptor_groups','group_id','groups_lookup','id'); - - $this->addForeignKey('permission_descriptor_roles','descriptor_id','permission_descriptors','id'); - $this->addForeignKey('permission_descriptor_roles','role_id','roles','id'); - - $this->addForeignKey('permission_descriptor_users','descriptor_id','permission_descriptors','id'); - $this->addForeignKey('permission_descriptor_users','user_id','users','id'); - - $this->addForeignKey('permission_dynamic_assignments','dynamic_condition_id','permission_dynamic_conditions','id'); - $this->addForeignKey('permission_dynamic_assignments','permission_id','permissions','id'); - - $this->addForeignKey('permission_dynamic_conditions','permission_object_id','permission_objects','id'); - $this->addForeignKey('permission_dynamic_conditions','group_id','groups_lookup','id'); - $this->addForeignKey('permission_dynamic_conditions','condition_id','saved_searches','id'); - - $this->addForeignKey('permission_lookup_assignments','permission_id','permissions','id'); - $this->addForeignKey('permission_lookup_assignments','permission_lookup_id','permission_lookups','id'); // duplicate - $this->addForeignKey('permission_lookup_assignments','permission_descriptor_id','permission_descriptors','id'); - - $this->addForeignKey('plugin_rss','user_id','users','id'); - - $this->addForeignKey('quicklinks','user_id','users','id'); - - $this->addForeignKey('role_allocations','folder_id','folders','id'); - $this->addForeignKey('role_allocations','role_id', 'roles','id'); - $this->addForeignKey('role_allocations','permission_descriptor_id','permission_descriptors','id'); - - $this->addForeignKey('saved_searches','user_id','users','id'); - - $this->addForeignKey('search_document_user_link','document_id','documents','id'); - $this->addForeignKey('search_document_user_link','user_id','users','id'); - - $this->addForeignKey('search_saved','user_id','users','id'); - $this->addForeignKey('search_saved_events','document_id','documents','id'); - - $this->addForeignKey('time_period','time_unit_id','time_unit_lookup','id'); - - $this->addForeignKey('type_workflow_map','document_type_id','document_types_lookup','id'); - $this->addForeignKey('type_workflow_map','workflow_id','workflows','id'); - - $this->addForeignKey('units_lookup','folder_id','folders','id'); - - $this->addForeignKey('units_organisations_link','unit_id','units_lookup','id'); - $this->addForeignKey('units_organisations_link','organisation_id','organisations_lookup','id'); - - $this->addForeignKey('uploaded_files','userid','users','id'); - $this->addForeignKey('uploaded_files','document_id','documents','id'); - - $this->addForeignKey('user_history','user_id','users','id'); - - $this->addForeignKey('user_history_documents','document_id','documents','id'); - $this->addForeignKey('user_history_documents','user_id','users','id'); - - $this->addForeignKey('user_history_folders','folder_id','folders','id'); - $this->addForeignKey('user_history_folders','user_id','users','id'); - - $this->addForeignKey('users','authentication_source_id','authentication_sources','id'); - - $this->addForeignKey('users_groups_link', 'user_id','users','id'); - $this->addForeignKey('users_groups_link', 'group_id','groups_lookup', 'id'); - - $this->addForeignKey('workflow_documents','document_id', 'documents','id'); - $this->addForeignKey('workflow_documents','workflow_id', 'workflows','id'); - $this->addForeignKey('workflow_documents','state_id','workflow_states','id'); - - $this->addForeignKey('workflow_state_actions','state_id','workflow_states','id'); - - $this->addForeignKey('workflow_state_disabled_actions','state_id','workflow_states','id'); - - $this->addForeignKey('workflow_state_permission_assignments','permission_id','permissions','id'); - $this->addForeignKey('workflow_state_permission_assignments','permission_descriptor_id','permission_descriptors','id'); - $this->addForeignKey('workflow_state_permission_assignments','workflow_state_id','workflow_states','id'); - - $this->addForeignKey('workflow_state_transitions','state_id','workflow_states','id'); - $this->addForeignKey('workflow_state_transitions','transition_id','workflow_transitions','id'); - - $this->addForeignKey('workflow_states','workflow_id', 'workflows','id'); - $this->addForeignKey('workflow_states','inform_descriptor_id', 'permission_descriptors','id'); - - $this->addForeignKey('workflow_transitions','workflow_id','workflows','id'); - $this->addForeignKey('workflow_transitions','target_state_id','workflow_states','id'); - $this->addForeignKey('workflow_transitions','guard_permission_id','permissions','id'); - $this->addForeignKey('workflow_transitions','guard_condition_id','saved_searches','id'); - $this->addForeignKey('workflow_transitions','guard_group_id','groups_lookup','id'); - $this->addForeignKey('workflow_transitions','guard_role_id','roles','id'); - - $this->addForeignKey('workflow_trigger_instances','workflow_transition_id','workflow_transitions','id'); - - $this->addForeignKey('workflows','start_state_id','workflow_states','id'); - - } - - function removeDuplicateIndexes() - { - foreach($this->primary as $table=>$key) - { - $this->dropIndex($table,$key); - } - - } - function addOtherIndexes() - { - $this->addIndex('active_sessions', 'session_id'); - $this->addIndex('authentication_sources','namespace'); - - $this->addIndex('column_entries','view_namespace'); - - $this->addIndex('comment_searchable_text', 'body', 'FULLTEXT'); - - - $this->addIndex('dashlet_disables','dashlet_namespace'); - $this->addIndex('document_content_version','storage_path'); - - $this->addIndex('document_metadata_version','version_created'); - $this->addIndex('document_role_allocations', array('document_id', 'role_id')); - - $this->addIndex('document_searchable_text','document_text', 'FULLTEXT'); - - $this->addIndex('document_text','document_text', 'FULLTEXT'); - $this->addIndex('document_transaction_text','document_text', 'FULLTEXT'); - - $this->addIndex('document_transaction_types_lookup','namespace', 'UNIQUE'); - - $this->addIndex('document_transactions','session_id'); - $this->addIndex('document_transactions','document_id'); - - $this->addIndex('document_types_lookup','name'); - //$this->addIndex('document_types_lookup','disabled'); ? used - - $this->addIndex('documents','created'); - $this->addIndex('documents','modified'); - $this->addIndex('documents','full_path','','(255)'); - $this->addIndex('documents','immutable'); - $this->addIndex('documents','checkedout'); - - $this->addIndex('document_content_version','filename','','(255)'); - $this->addIndex('document_content_version','size'); - - $this->addIndex('field_behaviour_options',array('behaviour_id','field_id')); - - $this->addIndex('field_behaviours','name'); - - $this->addIndex('fieldsets','is_generic'); - $this->addIndex('fieldsets','is_complete'); - $this->addIndex('fieldsets','is_system'); - - $this->addIndex('field_orders','child_field_id', 'UNIQUE'); - - $this->addIndex('folder_searchable_text','folder_text' ,'FULLTEXT'); - - $this->addIndex('folder_transactions','folder_id'); - $this->addIndex('folder_transactions','session_id'); - -// $this->addIndex('folders','name'); - $this->addIndex('folders', array('parent_id','name')); - - $this->addIndex('groups_lookup','name', 'UNIQUE'); - $this->addIndex('groups_lookup', array('authentication_source_id','authentication_details_s1')); - - $this->addIndex('interceptor_instances','interceptor_namespace'); // unique? - - $this->addIndex('metadata_lookup','disabled'); - //$this->addNewIndex('metadata_lookup','is_stuck'); don't think this is used anywhere.... - - $this->addIndex('metadata_lookup_tree','metadata_lookup_tree_parent'); - - $this->addIndex('mime_types','filetypes'); - $this->addIndex('mime_types','mimetypes'); - - $this->addIndex('notifications','data_int_1'); // document id seems to be stored in this. used by clearnotifications. -// $this->addIndex('notifications','type'); // don't think this is used - - $this->addIndex('organisations_lookup','name', 'UNIQUE'); - - $this->addIndex('permission_assignments', array('permission_object_id','permission_id'), 'UNIQUE'); // note change of order -// $this->dropIndex('permission_assignments','permission_object_id'); // duplicate - - //$this->dropIndex('permission_descriptor_groups','descriptor_id'); // in primary key - $this->addIndex('permission_descriptor_groups','group_id'); - - //$this->dropIndex('permission_descriptor_roles','descriptor_id'); // in primary key - $this->addIndex('permission_descriptor_roles','role_id'); - - //$this->dropIndex('permission_descriptor_users','descriptor_id'); // in primary - $this->addIndex('permission_descriptor_users','user_id'); - - $this->addIndex('permission_descriptors','descriptor','UNIQUE'); - - $this->addIndex('permission_lookup_assignments', array('permission_lookup_id', 'permission_id'), 'UNIQUE'); - //$this->dropIndex('permission_lookup_assignments','permission_lookup_id'); // in composite - - $this->addIndex('permissions','name', 'UNIQUE'); - - $this->addIndex('plugins','namespace','UNIQUE'); - $this->addIndex('plugins','disabled'); - - $this->addIndex('quicklinks','target_id'); - - $this->addIndex('roles','name','UNIQUE'); - $this->addIndex('saved_searches','namespace','UNIQUE'); - - $this->addIndex('scheduler_tasks','task', 'UNIQUE'); - - $this->addIndex('system_settings','name', 'UNIQUE'); - - $this->addIndex('units_lookup','name' ,'UNIQUE'); -// $this->dropIndex('units_lookup','folder_id'); - $this->addIndex('units_lookup','folder_id' ,'UNIQUE'); - - $this->addIndex('upgrades','descriptor'); - $this->addIndex('upgrades','parent'); - - $this->addIndex('user_history','action_namespace'); - $this->addIndex('user_history','datetime'); - $this->addIndex('user_history','session_id'); - - $this->addIndex('user_history_documents', array('user_id','document_id')); - //$this->dropIndex('user_history_documents', 'user_id'); // duplicate - - $this->addIndex('user_history_folders', array('user_id','folder_id')); - //$this->dropIndex('user_history_folders', 'user_id'); // duplicate - - $this->addIndex('users','username' ,'UNIQUE'); - $this->addIndex('users','authentication_source_id'); - //$this->addNewIndex('users','authentication_details_b1'); - //$this->addNewIndex('users','authentication_details_b2'); - $this->addIndex('users','last_login'); - $this->addIndex('users','disabled'); - - $this->addIndex('workflow_states','name'); - $this->addIndex('workflow_states','inform_descriptor_id'); //? - - $this->addIndex('workflow_transitions',array('workflow_id','name'), 'UNIQUE'); - //$this->dropIndex('workflow_transitions','workflow_id'); // duplicate - $this->addIndex('workflow_transitions','name'); - $this->addIndex('workflow_transitions','guard_permission_id'); //? - - $this->addIndex('workflow_trigger_instances','namespace'); - - $this->addIndex('workflows','name', 'UNIQUE'); - - - } - - function applyPostFixes() - { - - } - - function dropIndex($table, $field) - { - if (!is_array($fields)) $field = array($field); - $field = implode('_', $field); - $sql = "alter table $table drop index $field"; - $this->_exec($sql); - } - - function addIndex($table, $fields, $type='', $extra='') - { - if (!in_array($table, $this->tables)) return; - - if (!is_array($fields)) $fields = array($fields); - $index = implode('_', $fields); - //$index = str_replace('_id','',$index); - $fields = implode(',',$fields); - $sql = "alter table $table add $type index $index ($fields$extra) "; - $this->_exec($sql); - } - - function addForeignKey($table, $field, $othertable, $otherfield, $ondelete='cascade', $onupdate='cascade') - { - if (!in_array($table, $this->tables)) return; - if (!in_array($othertable, $this->tables)) return; - - $sql = "alter table $table add foreign key ($field) references $othertable ($otherfield) "; - if ($ondelete != '') - $sql .= " on delete $ondelete"; - if ($onupdate != '') - $sql .= " on update $onupdate"; - $this->_exec($sql); - } - - function addPrimaryKeys() - { - $this->addPrimaryKey('active_sessions', 'id'); - $this->addPrimaryKey('archive_restoration_request','id'); - $this->addPrimaryKey('archiving_settings','id'); - $this->addPrimaryKey('archiving_type_lookup','id'); - $this->addPrimaryKey('authentication_sources','id'); - $this->addPrimaryKey('baobab_keys','id'); - $this->addPrimaryKey('baobab_user_keys','id'); - $this->addPrimaryKey('column_entries','id'); - $this->addPrimaryKey('comment_searchable_text','comment_id'); - $this->addPrimaryKey('dashlet_disables','id'); - $this->addPrimaryKey('data_types','id'); - $this->addPrimaryKey('discussion_comments','id'); - $this->addPrimaryKey('discussion_threads','id'); - $this->addPrimaryKey('document_archiving_link','id'); - $this->addPrimaryKey('document_content_version','id'); - $this->addPrimaryKey('document_fields','id'); - $this->addPrimaryKey('document_fields_link','id'); - $this->addPrimaryKey('document_incomplete','id'); - $this->addPrimaryKey('document_link','id'); - $this->addPrimaryKey('document_link_types','id'); - $this->addPrimaryKey('document_metadata_version','id'); - $this->addPrimaryKey('document_role_allocations','id'); - $this->addPrimaryKey('document_subscriptions','id'); - $this->addPrimaryKey('document_tags',array('document_id','tag_id')); - $this->addPrimaryKey('document_text', 'document_id'); - $this->addPrimaryKey('document_transaction_types_lookup', 'id'); - $this->addPrimaryKey('document_transaction_text', 'document_id'); - $this->addPrimaryKey('document_transactions','id'); - $this->addPrimaryKey('document_type_fields_link','id'); - $this->addPrimaryKey('document_type_fieldsets_link','id'); - $this->addPrimaryKey('document_types_lookup','id'); - $this->addPrimaryKey('documents','id'); - $this->addPrimaryKey('download_files',array('document_id','session')); - $this->addPrimaryKey('field_behaviours','id'); - $this->addPrimaryKey('field_value_instances','id'); - $this->addPrimaryKey('fieldsets','id'); - $this->addPrimaryKey('folder_doctypes_link','id'); - $this->addPrimaryKey('folder_searchable_text','folder_id'); - $this->addPrimaryKey('folder_subscriptions','id'); - $this->addPrimaryKey('folder_transactions','id'); - $this->addPrimaryKey('folder_workflow_map','folder_id'); - $this->addPrimaryKey('folders','id'); - $this->addPrimaryKey('folders_users_roles_link','id'); - $this->addPrimaryKey('groups_groups_link','id'); - $this->addPrimaryKey('groups_lookup','id'); - $this->addPrimaryKey('help','id'); - $this->addPrimaryKey('help_replacement','id'); - $this->addPrimaryKey('index_files','document_id'); - $this->addPrimaryKey('interceptor_instances','id'); - $this->addPrimaryKey('links','id'); - $this->addPrimaryKey('metadata_lookup','id'); - $this->addPrimaryKey('metadata_lookup_tree','id'); - $this->addPrimaryKey('mime_documents','id'); - $this->addPrimaryKey('mime_extractors','id'); - $this->addPrimaryKey('mime_document_mapping',array('mime_type_id','mime_document_id')); - $this->addPrimaryKey('mime_types','id'); - $this->addPrimaryKey('news','id'); - $this->addPrimaryKey('notifications','id'); - $this->addPrimaryKey('organisations_lookup','id'); - $this->addPrimaryKey('permission_assignments','id'); - $this->addPrimaryKey('permission_descriptor_groups', array('descriptor_id','group_id')); - $this->addPrimaryKey('permission_descriptor_roles', array('descriptor_id','role_id')); - $this->addPrimaryKey('permission_descriptor_users', array('descriptor_id','user_id')); - $this->addPrimaryKey('permission_descriptors','id'); - $this->addPrimaryKey('permission_dynamic_conditions','id'); - $this->addPrimaryKey('permission_lookup_assignments','id'); - $this->addPrimaryKey('permission_lookups','id'); - $this->addPrimaryKey('permission_objects','id'); - $this->addPrimaryKey('permissions','id'); - $this->addPrimaryKey('plugin_rss','id'); - $this->addPrimaryKey('plugins','id'); - $this->addPrimaryKey('quicklinks','id'); - $this->addPrimaryKey('role_allocations','id'); - $this->addPrimaryKey('roles','id'); - $this->addPrimaryKey('saved_searches','id'); - $this->addPrimaryKey('scheduler_tasks','id'); - $this->addPrimaryKey('search_ranking',array('groupname','itemname')); - $this->addPrimaryKey('search_saved','id'); - $this->addPrimaryKey('search_saved_events','document_id'); - $this->addPrimaryKey('status_lookup','id'); - $this->addPrimaryKey('system_settings','id'); - $this->addPrimaryKey('tag_words','id'); - $this->addPrimaryKey('time_period','id'); - $this->addPrimaryKey('time_unit_lookup','id'); - $this->addPrimaryKey('trigger_selection','event_ns'); - $this->addPrimaryKey('type_workflow_map','document_type_id'); - $this->addPrimaryKey('units_lookup','id'); - $this->addPrimaryKey('units_organisations_link','id'); - $this->addPrimaryKey('upgrades','id'); - $this->addPrimaryKey('uploaded_files','tempfilename'); - $this->addPrimaryKey('user_history','id'); - $this->addPrimaryKey('user_history_documents','id'); - $this->addPrimaryKey('user_history_folders','id'); - $this->addPrimaryKey('users','id'); - $this->addPrimaryKey('users_groups_link','id'); - $this->addPrimaryKey('workflow_actions','workflow_id'); - $this->addPrimaryKey('workflow_documents','document_id'); - $this->addPrimaryKey('workflow_state_permission_assignments','id'); - $this->addPrimaryKey('workflow_states','id'); - $this->addPrimaryKey('workflow_state_transitions',array('state_id','transition_id')); - $this->addPrimaryKey('workflow_transitions','id'); - $this->addPrimaryKey('workflow_trigger_instances','id'); - $this->addPrimaryKey('workflows','id'); - } - - function addPrimaryKey($table, $primarykey) - { - if (!in_array($table, $this->tables)) return; - if (is_array($primarykey)) - { - $primarykey = implode(',', $primarykey); - } - - $sql="alter table $table add primary key ($primarykey)"; - $this->_exec($sql, false); - - if (strpos($primarykey,',') === false) - { - $this->primary[$table] = $primarykey; - $sql="alter table $table add unique key ($primarykey)"; - $this->_exec($sql); - } - } - - function dropIndexes() - { - $result = DBUtil::getResultArray("show tables"); - $tables=array(); - $this->tables = array(); - - foreach($result as $table) - { - $keys = array_keys($table); - - $tablename = $table[$keys[0]]; - if (substr($tablename,0,5) == 'zseq_') - { - continue; - } - - $stmt = DBUtil::getResultArray("show create table $tablename"); - $this->tables[] = $tablename; - - $keys = array_keys($stmt[0]); - - $sql = $stmt[0][$keys[1]]; - - $table = array('fks'=>array(), 'pk'=>array(), 'keys'=>array()); - $lines = explode("\n", $sql); - foreach($lines as $line) - { - $line = trim($line); - if (strpos($line, 'PRIMARY KEY') === 0) - { - preg_match('(\`([^\`])*\`)',$line, $params); - $primaryKey = explode(',', $params[0]); - foreach($primaryKey as $value) - { - $fieldname = substr($value,1,-1); - $table['pk'][] = $fieldname; - } - continue; - } - elseif (strpos($line, 'CONSTRAINT') === 0) - { - preg_match_all('(\`([^\`])*\`)',$line, $params); - - $fieldname = substr($params[0][1],1,-1); - - $table['fks'][$fieldname] = array( - 'constraint'=>substr($params[0][0],1,-1), - 'table'=>substr($params[0][2],1,-1), - 'field'=>substr($params[0][3],1,-1) - ); - continue; - } - elseif (strpos($line, 'KEY') !== false) - { - preg_match_all('(\`([^\`])*\`)',$line, $params); - $fieldname = substr($params[0][1],1,-1); - $key = substr($params[0][0],1,-1); - $table['keys'][$fieldname] = array('name'=>$key, 'unique'=>false); - if (strpos($line, 'UNIQUE KEY') !== false) - { - if (count($params[0]) == 2) - { - $table['keys'][$fieldname]['unique']=true; - } - } - continue; - } - } - - $tables[$tablename]= $table; - } - - $dropped = 0; - - // drop foreign keys - foreach($tables as $tablename=>$table) - { - foreach($table['fks'] as $fieldname=>$constraint) - { - $name = $constraint['constraint']; - $table = $constraint['table']; - $field = $constraint['field']; - $sql = "ALTER TABLE $tablename DROP FOREIGN KEY $name;"; - if ($this->_exec($sql)) $dropped++; - } - } - - // drop primary keys - foreach($tables as $tablename=>$table) - { - foreach($table['pk'] as $fieldname) - { - $sql = "ALTER TABLE $tablename DROP primary key;"; - if ($this->_exec($sql,false)) $dropped++; - break; - } - } - - // drop normal indexes - - foreach($tables as $tablename=>$table) - { - foreach($table['keys'] as $fieldname=>$keyinfo) - { - $keyname = $keyinfo['name']; - $sql = "ALTER TABLE $tablename DROP key $keyname;"; - if ($this->_exec($sql)) $dropped++; - break; - } - } - - - return $dropped; - } - - function _exec($sql, $report = true) - { - print "Action: $sql"; - $this->start(); - $rs = DBUtil::runQuery($sql); - print " - " . $this->end() . "\n"; - if (PEAR::isError($rs)) - { - if ($report) print "* " . $rs->getMessage() . "\n"; - return false; - } - return true; - } - -} +ini_set('display_errors','Off'); +$schemautil = KTSchemaUtil::getSingleton(); +$schemautil->dropForeignKeys(); +$schemautil->dropPrimaryKeys(); +$schemautil->dropIndexes(); +$schemautil->createPrimaryKeys(); +$schemautil->createForeignKeys(); +$schemautil->createIndexes(); ?> \ No newline at end of file diff --git a/lib/database/schema.inc.php b/lib/database/schema.inc.php new file mode 100644 index 0000000..16c460c --- /dev/null +++ b/lib/database/schema.inc.php @@ -0,0 +1,1001 @@ +dropForeignKeys(); +//$schemautil->dropPrimaryKeys(); +//$schemautil->dropIndexes(); +//$schemautil->createPrimaryKeys(); +//$schemautil->createForeignKeys(); +//$schemautil->createIndexes(); + +class KTSchemaUtil +{ + /** + * Indicates if statements to the database must be performed. + * + * @var boolean + */ + public $persist; + + /** + * Primary key definitions. + * + * @var array + */ + private $primaryKeys; + + /** + * Foreign key definitions + * + * @var array + */ + private $foreignKeys; + + /** + * Index definitions + * + * @var array + */ + private $indexes; + + /** + * Schema of database + * + * @var array + */ + private $schema; + + private $primary; + + + private function __construct($setup=false) + { + $this->persist = true; + + $this->getDBSchema(); + if ($setup) + { + $this->setupAdminDatabase(); + } + + $this->definePrimaryKeys(); + $this->defineForeignKeys(); + $this->defineOtherIndexes(); + $this->_exec('SET FOREIGN_KEY_CHECKS = 0'); + } + + public function __destruct() + { + $this->_exec('SET FOREIGN_KEY_CHECKS = 1'); + } + + private function createFixUser() + { + $sql = "SELECT 1 FROM users WHERE id = -10;"; + $rs = DBUtil::getResultArray($sql); + if (PEAR::isError($rs)) + { + print ''; + } + if (count($rs) == 0) + { + $sql = "INSERT INTO users (id,username,name,password,max_sessions) VALUES (-10,'_deleted_helper','Deleted User','---------------',0)"; + $this->_exec($sql); + } + } + + public function setupAdminDatabase() + { + global $default; + $dsn = array( + 'phptype' => $default->dbType, + 'username' => $default->dbAdminUser, + 'password' => $default->dbAdminPass, + 'hostspec' => $default->dbHost, + 'database' => $default->dbName, + 'port' => $default->dbPort, + ); + + $options = array( + 'debug' => 2, + 'portability' => DB_PORTABILITY_ERRORS, + 'seqname_format' => 'zseq_%s', + ); + + $default->_admindb = &DB::connect($dsn, $options); + if (PEAR::isError($default->_admindb)) + { + die($default->_admindb->toString()); + } + $default->_admindb->setFetchMode(DB_FETCHMODE_ASSOC); + return; + } + + private function removeDuplicateIndexes() + { + foreach($this->primary as $table=>$key) + { + $this->dropIndex($table,$key); + } + + } + + + /** + * Enter description here... + * + * @return KTSchemaUtil + */ + public function getSingleton() + { + static $singleton = null; + if (is_null($singleton)) + { + $singleton = new KTSchemaUtil(); + } + return $singleton; + } + + /** + * Adds primary keys to the database + * + */ + private function definePrimaryKeys() + { + $this->primaryKeys = array(); + $this->definePrimaryKey('active_sessions', 'id'); + $this->definePrimaryKey('archive_restoration_request','id'); + $this->definePrimaryKey('archiving_settings','id'); + $this->definePrimaryKey('archiving_type_lookup','id'); + $this->definePrimaryKey('authentication_sources','id'); + $this->definePrimaryKey('baobab_keys','id'); + $this->definePrimaryKey('baobab_user_keys','id'); + $this->definePrimaryKey('column_entries','id'); + $this->definePrimaryKey('comment_searchable_text','comment_id'); + $this->definePrimaryKey('dashlet_disables','id'); + $this->definePrimaryKey('data_types','id'); + $this->definePrimaryKey('discussion_comments','id'); + $this->definePrimaryKey('discussion_threads','id'); + $this->definePrimaryKey('document_archiving_link','id'); + $this->definePrimaryKey('document_content_version','id'); + $this->definePrimaryKey('document_fields','id'); + $this->definePrimaryKey('document_fields_link','id'); + $this->definePrimaryKey('document_incomplete','id'); + $this->definePrimaryKey('document_link','id'); + $this->definePrimaryKey('document_link_types','id'); + $this->definePrimaryKey('document_metadata_version','id'); + $this->definePrimaryKey('document_role_allocations','id'); + $this->definePrimaryKey('document_subscriptions','id'); + $this->definePrimaryKey('document_tags',array('document_id','tag_id')); + $this->definePrimaryKey('document_text', 'document_id'); + $this->definePrimaryKey('document_transaction_types_lookup', 'id'); + $this->definePrimaryKey('document_transaction_text', 'document_id'); + $this->definePrimaryKey('document_transactions','id'); + $this->definePrimaryKey('document_type_fields_link','id'); + $this->definePrimaryKey('document_type_fieldsets_link','id'); + $this->definePrimaryKey('document_types_lookup','id'); + $this->definePrimaryKey('documents','id'); + $this->definePrimaryKey('download_files',array('document_id','session')); + $this->definePrimaryKey('field_behaviours','id'); + $this->definePrimaryKey('field_value_instances','id'); + $this->definePrimaryKey('fieldsets','id'); + $this->definePrimaryKey('folder_doctypes_link','id'); + $this->definePrimaryKey('folder_searchable_text','folder_id'); + $this->definePrimaryKey('folder_subscriptions','id'); + $this->definePrimaryKey('folder_transactions','id'); + $this->definePrimaryKey('folder_workflow_map','folder_id'); + $this->definePrimaryKey('folders','id'); + $this->definePrimaryKey('folders_users_roles_link','id'); + $this->definePrimaryKey('groups_groups_link','id'); + $this->definePrimaryKey('groups_lookup','id'); + $this->definePrimaryKey('help','id'); + $this->definePrimaryKey('help_replacement','id'); + $this->definePrimaryKey('index_files','document_id'); + $this->definePrimaryKey('interceptor_instances','id'); + $this->definePrimaryKey('links','id'); + $this->definePrimaryKey('metadata_lookup','id'); + $this->definePrimaryKey('metadata_lookup_tree','id'); + $this->definePrimaryKey('mime_documents','id'); + $this->definePrimaryKey('mime_extractors','id'); + $this->definePrimaryKey('mime_document_mapping',array('mime_type_id','mime_document_id')); + $this->definePrimaryKey('mime_types','id'); + $this->definePrimaryKey('news','id'); + $this->definePrimaryKey('notifications','id'); + $this->definePrimaryKey('organisations_lookup','id'); + $this->definePrimaryKey('permission_assignments','id'); + $this->definePrimaryKey('permission_descriptor_groups', array('descriptor_id','group_id')); + $this->definePrimaryKey('permission_descriptor_roles', array('descriptor_id','role_id')); + $this->definePrimaryKey('permission_descriptor_users', array('descriptor_id','user_id')); + $this->definePrimaryKey('permission_descriptors','id'); + $this->definePrimaryKey('permission_dynamic_conditions','id'); + $this->definePrimaryKey('permission_lookup_assignments','id'); + $this->definePrimaryKey('permission_lookups','id'); + $this->definePrimaryKey('permission_objects','id'); + $this->definePrimaryKey('permissions','id'); + $this->definePrimaryKey('plugin_rss','id'); + $this->definePrimaryKey('plugins','id'); + $this->definePrimaryKey('plugin_helper','id'); + $this->definePrimaryKey('quicklinks','id'); + $this->definePrimaryKey('role_allocations','id'); + $this->definePrimaryKey('roles','id'); + $this->definePrimaryKey('saved_searches','id'); + $this->definePrimaryKey('scheduler_tasks','id'); + $this->definePrimaryKey('search_ranking',array('groupname','itemname')); + $this->definePrimaryKey('search_saved','id'); + $this->definePrimaryKey('search_saved_events','document_id'); + $this->definePrimaryKey('status_lookup','id'); + $this->definePrimaryKey('system_settings','id'); + $this->definePrimaryKey('tag_words','id'); + $this->definePrimaryKey('time_period','id'); + $this->definePrimaryKey('time_unit_lookup','id'); + $this->definePrimaryKey('trigger_selection','event_ns'); + $this->definePrimaryKey('type_workflow_map','document_type_id'); + $this->definePrimaryKey('units_lookup','id'); + $this->definePrimaryKey('units_organisations_link','id'); + $this->definePrimaryKey('upgrades','id'); + $this->definePrimaryKey('uploaded_files','tempfilename'); + $this->definePrimaryKey('user_history','id'); + $this->definePrimaryKey('user_history_documents','id'); + $this->definePrimaryKey('user_history_folders','id'); + $this->definePrimaryKey('users','id'); + $this->definePrimaryKey('users_groups_link','id'); + $this->definePrimaryKey('workflow_actions','workflow_id'); + $this->definePrimaryKey('workflow_documents','document_id'); + $this->definePrimaryKey('workflow_state_permission_assignments','id'); + $this->definePrimaryKey('workflow_states','id'); + $this->definePrimaryKey('workflow_state_transitions',array('state_id','transition_id')); + $this->definePrimaryKey('workflow_transitions','id'); + $this->definePrimaryKey('workflow_trigger_instances','id'); + $this->definePrimaryKey('workflows','id'); + } + + /** + * Adds foreign keys to the database + * + */ + private function defineForeignKeys() + { + $this->foreignKeys = array(); + $this->defineForeignKey('active_sessions', 'user_id', 'users', 'id'); + + $this->defineForeignKey('archive_restoration_request', 'document_id', 'documents', 'id'); + $this->defineForeignKey('archive_restoration_request', 'request_user_id', 'users', 'id'); + $this->defineForeignKey('archive_restoration_request', 'admin_user_id', 'users', 'id'); + + $this->defineForeignKey('archiving_settings', 'archiving_type_id', 'archiving_type_lookup', 'id'); + $this->defineForeignKey('archiving_settings', 'time_period_id', 'time_period', 'id'); + + $this->defineForeignKey('baobab_user_keys', 'user_id', 'users', 'id'); + $this->defineForeignKey('baobab_user_keys', 'key_id', 'baobab_keys', 'id'); + + $this->defineForeignKey('comment_searchable_text', 'comment_id', 'discussion_comments', 'id'); + $this->defineForeignKey('comment_searchable_text', 'document_id', 'documents', 'id'); + + $this->defineForeignKey('dashlet_disables', 'user_id', 'users', 'id'); + + $this->defineForeignKey('discussion_comments', 'thread_id', 'discussion_threads', 'id'); + $this->defineForeignKey('discussion_comments', 'user_id', 'users', 'id'); + $this->defineForeignKey('discussion_comments', 'in_reply_to', 'discussion_comments', 'id'); + + $this->defineForeignKey('discussion_threads', 'document_id', 'documents', 'id'); + $this->defineForeignKey('discussion_threads', 'first_comment_id', 'discussion_comments', 'id'); + $this->defineForeignKey('discussion_threads', 'last_comment_id', 'discussion_comments', 'id'); + $this->defineForeignKey('discussion_threads', 'creator_id', 'users', 'id'); + + $this->defineForeignKey('document_archiving_link', 'document_id', 'documents', 'id'); + $this->defineForeignKey('document_archiving_link', 'archiving_settings_id', 'archiving_settings', 'id'); + + $this->defineForeignKey('document_content_version', 'document_id', 'documents', 'id'); + $this->defineForeignKey('document_content_version', 'mime_id', 'mime_types', 'id'); + + $this->defineForeignKey('document_fields','parent_fieldset','fieldsets','id'); + + $this->defineForeignKey('document_fields_link','document_field_id','document_fields','id'); + $this->defineForeignKey('document_fields_link','metadata_version_id','document_metadata_version','id'); + + $this->defineForeignKey('document_link','parent_document_id', 'documents', 'id'); + $this->defineForeignKey('document_link','child_document_id', 'documents', 'id'); + $this->defineForeignKey('document_link','link_type_id','document_link_types','id'); + + $this->defineForeignKey('document_metadata_version','document_type_id','document_types_lookup','id'); + $this->defineForeignKey('document_metadata_version','status_id','status_lookup','id'); + $this->defineForeignKey('document_metadata_version','document_id','documents','id'); + $this->defineForeignKey('document_metadata_version','version_creator_id','users','id'); + $this->defineForeignKey('document_metadata_version','content_version_id','document_content_version','id'); + $this->defineForeignKey('document_metadata_version','workflow_id','workflows','id'); + $this->defineForeignKey('document_metadata_version','workflow_state_id','workflow_states','id'); + + $this->defineForeignKey('document_role_allocations','role_id','roles','id'); + $this->defineForeignKey('document_role_allocations','permission_descriptor_id','permission_descriptors','id'); + + $this->defineForeignKey('document_searchable_text','document_id','documents','id'); + + $this->defineForeignKey('document_subscriptions','user_id','users','id'); + $this->defineForeignKey('document_subscriptions','document_id','documents','id'); + + $this->defineForeignKey('document_tags','document_id','documents','id'); + $this->defineForeignKey('document_tags','tag_id','tag_words','id'); + + $this->defineForeignKey('document_text','document_id','documents','id'); + + + $this->defineForeignKey('document_transaction_text','document_id','documents','id'); + + $this->defineForeignKey('document_type_fields_link','document_type_id', 'document_types_lookup','id'); + $this->defineForeignKey('document_type_fields_link','field_id','document_fields','id'); + + $this->defineForeignKey('document_type_fieldsets_link','document_type_id', 'document_types_lookup','id'); + $this->defineForeignKey('document_type_fieldsets_link','fieldset_id','fieldsets','id'); + + $this->defineForeignKey('documents','creator_id','users','id', 'SET NULL', 'SET NULL'); + $this->defineForeignKey('documents','folder_id','folders','id'); + $this->defineForeignKey('documents','checked_out_user_id','users','id', 'SET NULL', 'SET NULL'); + $this->defineForeignKey('documents','status_id','status_lookup','id'); + $this->defineForeignKey('documents','permission_object_id','permission_objects','id'); + $this->defineForeignKey('documents','permission_lookup_id','permission_lookups','id'); + $this->defineForeignKey('documents','modified_user_id','users','id', 'SET NULL', 'SET NULL'); + $this->defineForeignKey('documents','metadata_version_id','document_metadata_version','id'); + + $this->defineForeignKey('download_files','document_id','documents','id'); + + $this->defineForeignKey('field_behaviour_options','behaviour_id','field_behaviours','id'); + $this->defineForeignKey('field_behaviour_options','field_id','document_fields','id'); + $this->defineForeignKey('field_behaviour_options','instance_id','field_value_instances','id'); + + $this->defineForeignKey('field_behaviours','field_id','document_fields','id'); + + $this->defineForeignKey('field_orders','child_field_id','document_fields','id'); + $this->defineForeignKey('field_orders','parent_field_id','document_fields','id'); + $this->defineForeignKey('field_orders','fieldset_id','fieldsets','id'); + + $this->defineForeignKey('field_value_instances','field_value_id','metadata_lookup','id'); // it is so.. strange ;) + $this->defineForeignKey('field_value_instances','behaviour_id','field_behaviours','id'); + $this->defineForeignKey('field_value_instances','field_id','document_fields','id'); + + $this->defineForeignKey('fieldsets','master_field','document_fields','id'); + + $this->defineForeignKey('folder_descendants','parent_id','folders','id'); + $this->defineForeignKey('folder_descendants','folder_id','folders','id'); + + $this->defineForeignKey('folder_doctypes_link','folder_id','folders','id'); + $this->defineForeignKey('folder_doctypes_link','document_type_id','document_types_lookup','id'); + + $this->defineForeignKey('folder_searchable_text','folder_id','folders','id'); + + $this->defineForeignKey('folder_subscriptions','user_id','users','id'); + $this->defineForeignKey('folder_subscriptions','folder_id','folders','id'); + + $this->defineForeignKey('folder_workflow_map','folder_id', 'folders','id'); + $this->defineForeignKey('folder_workflow_map','workflow_id', 'workflows','id'); + + $this->defineForeignKey('folders','creator_id','users','id'); + $this->defineForeignKey('folders','permission_object_id','permission_objects','id'); + $this->defineForeignKey('folders','permission_lookup_id','permission_lookups','id'); + $this->defineForeignKey('folders','parent_id','folders','id'); + + $this->defineForeignKey('folders_users_roles_link','user_id','users','id'); + $this->defineForeignKey('folders_users_roles_link','document_id','documents','id'); + + $this->defineForeignKey('groups_groups_link','parent_group_id','groups_lookup','id'); + $this->defineForeignKey('groups_groups_link','member_group_id','groups_lookup','id'); + + $this->defineForeignKey('groups_lookup','unit_id', 'units_lookup','id'); + + $this->defineForeignKey('index_files','document_id','documents','id'); + $this->defineForeignKey('index_files','user_id','users','id'); + + $this->defineForeignKey('metadata_lookup','document_field_id','document_fields','id'); + + $this->defineForeignKey('metadata_lookup_tree','document_field_id', 'document_fields','id'); + + $this->defineForeignKey('mime_types','mime_document_id','mime_documents','id', 'set null', 'set null'); + $this->defineForeignKey('mime_types','extractor_id','mime_extractors','id', 'set null', 'set null'); + + $this->defineForeignKey('mime_document_mapping','mime_type_id','mime_types','id'); + $this->defineForeignKey('mime_document_mapping','mime_document_id','mime_documents','id'); + + $this->defineForeignKey('news','image_mime_type_id','mime_types','id'); + + $this->defineForeignKey('notifications','user_id', 'users','id'); + + $this->defineForeignKey('permission_assignments','permission_id', 'permissions','id'); + $this->defineForeignKey('permission_assignments','permission_object_id','permission_objects','id'); // duplicate + $this->defineForeignKey('permission_assignments','permission_descriptor_id','permission_descriptors','id'); + + $this->defineForeignKey('permission_descriptor_groups','descriptor_id','permission_descriptors','id'); + $this->defineForeignKey('permission_descriptor_groups','group_id','groups_lookup','id'); + + $this->defineForeignKey('permission_descriptor_roles','descriptor_id','permission_descriptors','id'); + $this->defineForeignKey('permission_descriptor_roles','role_id','roles','id'); + + $this->defineForeignKey('permission_descriptor_users','descriptor_id','permission_descriptors','id'); + $this->defineForeignKey('permission_descriptor_users','user_id','users','id'); + + $this->defineForeignKey('permission_dynamic_assignments','dynamic_condition_id','permission_dynamic_conditions','id'); + $this->defineForeignKey('permission_dynamic_assignments','permission_id','permissions','id'); + + $this->defineForeignKey('permission_dynamic_conditions','permission_object_id','permission_objects','id'); + $this->defineForeignKey('permission_dynamic_conditions','group_id','groups_lookup','id'); + $this->defineForeignKey('permission_dynamic_conditions','condition_id','saved_searches','id'); + + $this->defineForeignKey('permission_lookup_assignments','permission_id','permissions','id'); + $this->defineForeignKey('permission_lookup_assignments','permission_lookup_id','permission_lookups','id'); // duplicate + $this->defineForeignKey('permission_lookup_assignments','permission_descriptor_id','permission_descriptors','id'); + + $this->defineForeignKey('plugin_rss','user_id','users','id'); + + $this->defineForeignKey('quicklinks','user_id','users','id'); + + $this->defineForeignKey('role_allocations','folder_id','folders','id'); + $this->defineForeignKey('role_allocations','role_id', 'roles','id'); + $this->defineForeignKey('role_allocations','permission_descriptor_id','permission_descriptors','id'); + + $this->defineForeignKey('saved_searches','user_id','users','id'); + + $this->defineForeignKey('search_document_user_link','document_id','documents','id'); + $this->defineForeignKey('search_document_user_link','user_id','users','id'); + + $this->defineForeignKey('search_saved','user_id','users','id'); + $this->defineForeignKey('search_saved_events','document_id','documents','id'); + + $this->defineForeignKey('time_period','time_unit_id','time_unit_lookup','id'); + + $this->defineForeignKey('type_workflow_map','document_type_id','document_types_lookup','id'); + $this->defineForeignKey('type_workflow_map','workflow_id','workflows','id'); + + $this->defineForeignKey('units_lookup','folder_id','folders','id'); + + $this->defineForeignKey('units_organisations_link','unit_id','units_lookup','id'); + $this->defineForeignKey('units_organisations_link','organisation_id','organisations_lookup','id'); + + $this->defineForeignKey('uploaded_files','userid','users','id'); + $this->defineForeignKey('uploaded_files','document_id','documents','id'); + + $this->defineForeignKey('user_history','user_id','users','id'); + + $this->defineForeignKey('user_history_documents','document_id','documents','id'); + $this->defineForeignKey('user_history_documents','user_id','users','id'); + + $this->defineForeignKey('user_history_folders','folder_id','folders','id'); + $this->defineForeignKey('user_history_folders','user_id','users','id'); + + $this->defineForeignKey('users','authentication_source_id','authentication_sources','id'); + + $this->defineForeignKey('users_groups_link', 'user_id','users','id'); + $this->defineForeignKey('users_groups_link', 'group_id','groups_lookup', 'id'); + + $this->defineForeignKey('workflow_documents','document_id', 'documents','id'); + $this->defineForeignKey('workflow_documents','workflow_id', 'workflows','id'); + $this->defineForeignKey('workflow_documents','state_id','workflow_states','id'); + + $this->defineForeignKey('workflow_state_actions','state_id','workflow_states','id'); + + $this->defineForeignKey('workflow_state_disabled_actions','state_id','workflow_states','id'); + + $this->defineForeignKey('workflow_state_permission_assignments','permission_id','permissions','id'); + $this->defineForeignKey('workflow_state_permission_assignments','permission_descriptor_id','permission_descriptors','id'); + $this->defineForeignKey('workflow_state_permission_assignments','workflow_state_id','workflow_states','id'); + + $this->defineForeignKey('workflow_state_transitions','state_id','workflow_states','id'); + $this->defineForeignKey('workflow_state_transitions','transition_id','workflow_transitions','id'); + + $this->defineForeignKey('workflow_states','workflow_id', 'workflows','id'); + $this->defineForeignKey('workflow_states','inform_descriptor_id', 'permission_descriptors','id'); + + $this->defineForeignKey('workflow_transitions','workflow_id','workflows','id'); + $this->defineForeignKey('workflow_transitions','target_state_id','workflow_states','id'); + $this->defineForeignKey('workflow_transitions','guard_permission_id','permissions','id'); + $this->defineForeignKey('workflow_transitions','guard_condition_id','saved_searches','id'); + $this->defineForeignKey('workflow_transitions','guard_group_id','groups_lookup','id'); + $this->defineForeignKey('workflow_transitions','guard_role_id','roles','id'); + + $this->defineForeignKey('workflow_trigger_instances','workflow_transition_id','workflow_transitions','id'); + + $this->defineForeignKey('workflows','start_state_id','workflow_states','id'); + } + + /** + * Adds indexes that are not defined automatically via foreign key constraints and also adds criteria such as uniqueness. + * + */ + private function defineOtherIndexes() + { + $this->indexes = array(); + $this->defineIndex('active_sessions', 'session_id'); + $this->defineIndex('authentication_sources','namespace'); + + $this->defineIndex('column_entries','view_namespace'); + + $this->defineIndex('comment_searchable_text', 'body', 'FULLTEXT'); + + + $this->defineIndex('dashlet_disables','dashlet_namespace'); + $this->defineIndex('document_content_version','storage_path'); + + $this->defineIndex('document_metadata_version','version_created'); + $this->defineIndex('document_role_allocations', array('document_id', 'role_id')); + + $this->defineIndex('document_searchable_text','document_text', 'FULLTEXT'); + + $this->defineIndex('document_text','document_text', 'FULLTEXT'); + $this->defineIndex('document_transaction_text','document_text', 'FULLTEXT'); + + $this->defineIndex('document_transaction_types_lookup','namespace', 'UNIQUE'); + + $this->defineIndex('document_transactions','session_id'); + $this->defineIndex('document_transactions','document_id'); + + $this->defineIndex('document_types_lookup','name'); + + $this->defineIndex('documents','created'); + $this->defineIndex('documents','modified'); + $this->defineIndex('documents','full_path','','(255)'); + $this->defineIndex('documents','immutable'); + $this->defineIndex('documents','checkedout'); + + $this->defineIndex('document_content_version','filename','','(255)'); + $this->defineIndex('document_content_version','size'); + + $this->defineIndex('field_behaviour_options',array('behaviour_id','field_id')); + + $this->defineIndex('field_behaviours','name'); + + $this->defineIndex('fieldsets','is_generic'); + $this->defineIndex('fieldsets','is_complete'); + $this->defineIndex('fieldsets','is_system'); + + $this->defineIndex('field_orders','child_field_id', 'UNIQUE'); + + $this->defineIndex('folder_searchable_text','folder_text' ,'FULLTEXT'); + + $this->defineIndex('folder_transactions','folder_id'); + $this->defineIndex('folder_transactions','session_id'); + $this->defineIndex('folders', array('parent_id','name')); + + $this->defineIndex('groups_lookup','name', 'UNIQUE'); + $this->defineIndex('groups_lookup', array('authentication_source_id','authentication_details_s1')); + + $this->defineIndex('interceptor_instances','interceptor_namespace'); + + $this->defineIndex('metadata_lookup','disabled'); + + $this->defineIndex('metadata_lookup_tree','metadata_lookup_tree_parent'); + + $this->defineIndex('mime_types','filetypes'); + $this->defineIndex('mime_types','mimetypes'); + + $this->defineIndex('notifications','data_int_1'); + + $this->defineIndex('organisations_lookup','name', 'UNIQUE'); + + $this->defineIndex('permission_assignments', array('permission_object_id','permission_id'), 'UNIQUE'); + + $this->defineIndex('permission_descriptor_groups','group_id'); + + $this->defineIndex('permission_descriptor_roles','role_id'); + + $this->defineIndex('permission_descriptor_users','user_id'); + + $this->defineIndex('permission_descriptors','descriptor','UNIQUE'); + + $this->defineIndex('permission_lookup_assignments', array('permission_lookup_id', 'permission_id'), 'UNIQUE'); + + $this->defineIndex('permissions','name', 'UNIQUE'); + + $this->defineIndex('plugins','namespace','UNIQUE'); + $this->defineIndex('plugins','disabled'); + + $this->defineIndex('plugin_helper','namespace'); + $this->defineIndex('plugin_helper','plugin'); + $this->defineIndex('plugin_helper','classtype'); + + + $this->defineIndex('quicklinks','target_id'); + + $this->defineIndex('roles','name','UNIQUE'); + $this->defineIndex('saved_searches','namespace','UNIQUE'); + + $this->defineIndex('scheduler_tasks','task', 'UNIQUE'); + + $this->defineIndex('system_settings','name', 'UNIQUE'); + + $this->defineIndex('units_lookup','name' ,'UNIQUE'); + $this->defineIndex('units_lookup','folder_id' ,'UNIQUE'); + + $this->defineIndex('upgrades','descriptor'); + $this->defineIndex('upgrades','parent'); + + $this->defineIndex('user_history','action_namespace'); + $this->defineIndex('user_history','datetime'); + $this->defineIndex('user_history','session_id'); + + $this->defineIndex('user_history_documents', array('user_id','document_id')); + + $this->defineIndex('user_history_folders', array('user_id','folder_id')); + + $this->defineIndex('users','username' ,'UNIQUE'); + $this->defineIndex('users','authentication_source_id'); + $this->defineIndex('users','last_login'); + $this->defineIndex('users','disabled'); + + $this->defineIndex('workflow_states','name'); + $this->defineIndex('workflow_states','inform_descriptor_id'); //? + + $this->defineIndex('workflow_transitions',array('workflow_id','name'), 'UNIQUE'); + $this->defineIndex('workflow_transitions','name'); + $this->defineIndex('workflow_transitions','guard_permission_id'); //? + + $this->defineIndex('workflow_trigger_instances','namespace'); + + $this->defineIndex('workflows','name', 'UNIQUE'); + } + + private function definePrimaryKey($table, $primaryKey) + { + $definition = new stdClass(); + $definition->table = $table; + $definition->primaryKey = $primaryKey; + $this->primaryKeys[] = $definition; + } + + private function defineForeignKey($table, $field, $otherTable, $otherField, $onDelete='cascade', $onUpdate='cascade') + { + $definition = new stdClass(); + $definition->table = $table; + $definition->field = $field; + $definition->otherTable = $otherTable; + $definition->otherField = $otherField; + $definition->onDelete = $onDelete; + $definition->onUpdate = $onUpdate; + $this->foreignKeys[] = $definition; + } + + private function defineIndex($table, $fields, $type='', $extra='') + { + $definition = new stdClass(); + $definition->table = $table; + $definition->fields = $fields; + $definition->type = $type; + $definition->extra = $extra; + $this->indexes[] = $definition; + } + + public function createPrimaryKeys() + { + foreach($this->primaryKeys as $primaryKey) + { + $this->createPrimaryKey($primaryKey->table, $primaryKey->primaryKey); + } + } + + /** + * Add a primary key to a table. + * + * @param string $tablename + * @param string $primaryKey + */ + private function createPrimaryKey($tablename, $primaryKey) + { + if (!array_key_exists($tablename, $this->schema)) + { + // if we don't know about the table, possibly it is in the commercial version. + // exit gracefully. + return; + } + + if (is_array($primaryKey)) + { + $primaryKey = implode(',', $primaryKey); + } + + $sql="ALTER TABLE $tablename ADD PRIMARY KEY ($primaryKey)"; + $this->_exec($sql, false); + + if (strpos($primaryKey,',') === false) + { + // for some reason, there seems to be a problem periodically when adding foreign key constraints + // unless there is a unique key. just a primary key isn't good enough for some reason. so for now, + // we add the unique key, doubling up the effort of the primary key. we can drop these indexes again + // later after the constraints have been added. + $this->primary[$tablename] = $primaryKey; + $sql="ALTER TABLE $tablename ADD UNIQUE KEY ($primaryKey)"; + $this->_exec($sql); + } + } + + public function createForeignKeys() + { + foreach($this->foreignKeys as $foreignKey) + { + $this->createForeignKey($foreignKey->table,$foreignKey->field,$foreignKey->otherTable,$foreignKey->otherField,$foreignKey->onDelete, $foreignKey->onUpdate); + } + } + + /** + * Add a foreign key constraint for a table. + * + * @param string $table + * @param string $field + * @param string $othertable + * @param string $otherfield + * @param string $ondelete + * @param string $onupdate + */ + private function createForeignKey($table, $field, $otherTable, $otherField, $onDelete='cascade', $onUpdate='cascade') + { + if (!array_key_exists($table, $this->schema) || !array_key_exists($otherTable, $this->schema)) + { + // if we don't know about the tables, possibly it is in the commercial version. + // exit gracefully. + return; + } + + $this->fixForeignKey($table, $field, $otherTable, $otherField); + + $sql = "ALTER TABLE $table ADD FOREIGN KEY ($field) REFERENCES $otherTable ($otherField) "; + if ($onDelete != '') + { + $sql .= " ON DELETE $onDelete"; + } + if ($onUpdate != '') + { + $sql .= " ON UPDATE $onUpdate"; + } + $this->_exec($sql); + } + + public function createIndexes() + { + foreach($this->indexes as $index) + { + $this->createIndex($index->table, $index->fields, $index->type, $index->extra); + } + $this->removeDuplicateIndexes(); + } + + private function fixForeignKey($table, $field, $otherTable, $otherField) + { + if ($otherTable == 'users' && $otherField == 'id') + { + $this->createFixUser(); + $sql = "UPDATE $table SET $field = -10 WHERE $field is not null and $field not in (select distinct id from users)"; + $this->_exec($sql); + return; + } + + $sql = "DELETE FROM $table WHERE $field is not null and $field not in (select distinct $otherField FROM $otherTable)"; + $this->_exec($sql); + } + + /** + * Add an index to a table. + * + * @param string $table + * @param array $fields + * @param string $type + * @param string $extra + */ + private function createIndex($table, $fields, $type='', $extra='') + { + if (!array_key_exists($table, $this->schema)) + { + // if we don't know about the tables, possibly it is in the commercial version. + // exit gracefully. + return; + } + + if (!is_array($fields)) + { + $fields = array($fields); + } + $index = implode('_', $fields); + $fields = implode(',',$fields); + $sql = "ALTER TABLE $table ADD $type INDEX $index ($fields$extra) "; + $this->_exec($sql); + } + + + /** + * Drop all indexes and foreign key constraints from the system. + * + * @return int The number of elements cleared. + */ + private function getDBSchema() + { + $this->schema = array(); + $result = DBUtil::getResultArray('SHOW TABLES'); + $tables=array(); + + foreach($result as $table) + { + $keys = array_keys($table); + + $tablename = $table[$keys[0]]; + if (substr($tablename,0,5) == 'zseq_') + { + continue; + } + + $stmt = DBUtil::getResultArray("SHOW CREATE TABLE $tablename;"); + + $keys = array_keys($stmt[0]); + + $sql = $stmt[0][$keys[1]]; + + $this->schema[$tablename] = $sql; + } + } + + private function dropForeignKey($tablename, $foreignKey) + { + $sql = "ALTER TABLE $tablename DROP FOREIGN KEY $foreignKey;"; + return $this->_exec($sql); + } + + /** + * Drops foreign keys based on the current schema + * + * @return int + */ + public function dropForeignKeys() + { + $dropped = 0; + foreach($this->schema as $tablename=>$schema) + { + $lines = explode("\n", $schema); + foreach($lines as $line) + { + if (strpos($line, 'CONSTRAINT') === false) + { + continue; + } + preg_match_all('(\`([^\`])*\`)',$line, $params); + + $constraint=substr($params[0][0],1,-1); + $table= substr($params[0][2],1,-1); + + ($this->dropForeignKey($tablename, $constraint)); + $dropped++; + } + } + return $dropped; + } + + /** + * Drops primary keys based on the current schema + * + * @return int + */ + public function dropPrimaryKeys() + { + $dropped = 0; + foreach($this->schema as $tablename=>$schema) + { + $lines = explode("\n", $schema); + foreach($lines as $line) + { + if (strpos($line, 'PRIMARY KEY') === false) + { + continue; + } + + ($this->dropPrimaryKey($tablename)); + $dropped++; + } + } + return $dropped; + } + + /** + * Drops the primary key from a table + * + * @param string $tablename + */ + private function dropPrimaryKey($tablename) + { + $sql = "ALTER TABLE $tablename DROP primary key;"; + return $this->_exec($sql,false); + } + + /** + * Drops indexes based on the current schema + * + * @return int + */ + public function dropIndexes() + { + $dropped = 0; + foreach($this->schema as $tablename=>$schema) + { + $lines = explode("\n", $schema); + foreach($lines as $line) + { + if (strpos($line, 'KEY') === false) + { + continue; + } + + if (strpos($line, 'PRIMARY KEY') !== false) + { + continue; + } + + if (strpos($line, 'FOREIGN KEY') !== false) + { + continue; + } + + preg_match_all('(\`([^\`])*\`)',$line, $params); + + $key = substr($params[0][0],1,-1); + ($this->dropIndex($tablename, $key)); + $dropped++; + } + } + return $dropped; + } + + + /** + * Drop an index from the database. + * + * @param string $table + * @param string $field + */ + function dropIndex($table, $field) + { + if (!is_array($fields)) $field = array($field); + $field = implode('_', $field); + $sql = "ALTER TABLE $table DROP INDEX $field"; + $result = $this->_exec($sql); + + if (!$result) + { + print "..."; + } + + return $result; + } + + /** + * Execute a db sql statement on the database. + * + * @param string $sql + * @return boolean + */ + private function _exec($sql ) + { + global $default; + if (!$this->persist) + { + print "$sql\n"; + return; + } + $this->log("Action: $sql"); + $rs = DBUtil::runQuery($sql, $default->_admindb ); + if (PEAR::isError($rs)) + { + $this->log("* " . $rs->getMessage()); + return false; + } + return true; + } + + /** + * Logs a message to the log file + * + * @param string $msg + */ + private function log($msg, $level='info') + { + global $default; + $default->log->$level('KTSchemaUtil: ' .$msg); + } +} + + +?> \ No newline at end of file diff --git a/lib/upgrades/UpgradeFunctions.inc.php b/lib/upgrades/UpgradeFunctions.inc.php index 3b64b7b..2b02b1c 100644 --- a/lib/upgrades/UpgradeFunctions.inc.php +++ b/lib/upgrades/UpgradeFunctions.inc.php @@ -37,6 +37,7 @@ require_once(KT_LIB_DIR . '/upgrades/Ini.inc.php'); require_once(KT_DIR . '/plugins/ktcore/scheduler/scheduler.php'); +require_once(KT_LIB_DIR . '/database/schema.inc.php'); class UpgradeFunctions { var $upgrades = array( @@ -56,6 +57,7 @@ class UpgradeFunctions { '3.1.5' => array('upgradeSavedSearches'), '3.1.6.3' => array('cleanupGroupMembership'), '3.5.0' => array('cleanupOldKTAdminVersionNotifier', 'updateConfigFile35', 'registerIndexingTasks'), + '3.5.2' => array('dropForeignKeys','dropPrimaryKeys','dropIndexes','createPrimaryKeys','createForeignKeys','createIndexes'), ); var $descriptions = array( @@ -77,7 +79,13 @@ class UpgradeFunctions { 'cleanupGroupMembership' => 'Cleanup any old references to missing groups, etc.', 'cleanupOldKTAdminVersionNotifier' => 'Cleanup any old files from the old KTAdminVersionNotifier', 'updateConfigFile35' => 'Update the config.ini file for 3.5', - 'registerIndexingTasks'=>'Register the required indexing background tasks' + 'registerIndexingTasks'=>'Register the required indexing background tasks', + 'dropForeignKeys'=>'Recreate db integrity: Drop foreign keys on the database', + 'dropPrimaryKeys'=>'Recreate db integrity:Drop primary keys on the database', + 'dropIndexes'=>'Recreate db integrity:Drop indexes on the database', + 'createPrimaryKeys'=>'Recreate db integrity:Create primary keys on the database', + 'createForeignKeys'=>'Recreate db integrity:Create foreign keys on the database', + 'createIndexes'=>'Recreate db integrity:Create indexes on the database' ); var $phases = array( "setPermissionFolder" => 1, @@ -87,8 +95,51 @@ class UpgradeFunctions { "fixUnits" => 1, 'applyDiscussionUpgrade' => -1, 'fixDocumentRoleAllocation' => -1, + 'dropForeignKeys'=>1, + 'dropPrimaryKeys'=>2, + 'dropIndexes'=>3, + 'createPrimaryKeys'=>4, + 'createForeignKeys'=>5, + 'createIndexes'=>6, ); + function dropForeignKeys() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->dropForeignKeys(); + } + + function dropPrimaryKeys() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->dropPrimaryKeys(); + } + + function dropIndexes() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->dropIndexes(); + } + + function createPrimaryKeys() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->createPrimaryKeys(); + } + + function createForeignKeys() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->createForeignKeys(); + } + + function createIndexes() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->createIndexes(); + } + + // {{{ _setPermissionFolder function _setPermissionFolder($iFolderId) { global $default; -- libgit2 0.21.4