/** * Process models * * @param array $options * @return array */ public static function process_models($options = []) { $result = ['success' => false, 'error' => [], 'hint' => [], 'data' => []]; do { // we need to process all dependencies first $dep = self::process_deps_all($options); if (!$dep['success']) { $result = $dep; $result['error'][] = 'You must fix all dependency related errors first before processing models.'; break; } // proccesing models if (empty($dep['data']['model_processed'])) { $result['error'][] = 'You do not have models to process!'; break; } $object_attributes = []; $object_relations = []; $object_forms = []; $flag_relation = application::get('dep.submodule.numbers.data.relations') ? true : false; $object_documentation = []; $object_import = []; $ddl = new numbers_backend_db_class_ddl(); // run 1 to deterine virtual tables $first = true; $virtual_models = $dep['data']['model_processed']; run_again: foreach ($virtual_models as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); foreach (['attributes', 'audit', 'addresses'] as $v0) { if ($model->{$v0}) { $v01 = $v0 . '_model'; $virtual_models[str_replace('_', '.', $model->{$v01})] = 'object_table'; } } } } if ($first) { $first = false; goto run_again; // some widgets have attributes } $dep['data']['model_processed'] = array_merge_hard($dep['data']['model_processed'], $virtual_models); $domains = object_data_domains::get_static(); // run 2 foreach ($dep['data']['model_processed'] as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); $temp_result = $ddl->process_table_model($model); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; // relation if ($flag_relation) { if (!empty($model->relation)) { $domain = $model->columns[$model->relation['field']]['domain'] ?? null; if (!empty($domain)) { $domain = str_replace('_sequence', '', $domain); $type = $domains[$domain]['type']; } else { $type = $model->columns[$model->relation['field']]['type']; } $object_relations[$k2] = ['rn_relattr_code' => $model->relation['field'], 'rn_relattr_name' => $model->title, 'rn_relattr_model' => $k2, 'rn_relattr_domain' => $domain, 'rn_relattr_type' => $type, 'rn_relattr_inactive' => !empty($model->relation['inactive']) ? 1 : 0]; } if (!empty($model->attributes)) { $object_attributes[$k2] = ['rn_attrmdl_code' => $k2, 'rn_attrmdl_name' => $model->title, 'rn_attrmdl_inactive' => 0]; } } } else { if ($v == 'object_sequence') { $temp_result = $ddl->process_sequence_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_function') { $temp_result = $ddl->process_function_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_extension') { $temp_result = $ddl->process_function_extension($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_import') { $object_import[$k2] = ['model' => $k2]; } } } } } } // if we have erros if (!empty($result['error'])) { break; } // db factory $db_factory = factory::get('db'); // we load objects from database $loaded_objects = []; foreach ($ddl->db_links as $k => $v) { $ddl_object = $db_factory[$k]['ddl_object']; $temp_result = $ddl_object->load_schema($k); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } else { $loaded_objects[$k] = $temp_result['data']; } } // if we have erros if (!empty($result['error'])) { break; } // get a list of all db links $db_link_list = array_unique(array_merge(array_keys($ddl->objects), array_keys($loaded_objects))); // if we are dropping schema if ($options['mode'] == 'drop') { $ddl->objects = []; } // compare schemas per db link $schema_diff = []; $total_per_db_link = []; $total = 0; foreach ($db_link_list as $k) { // we need to have a back end for comparison $compare_options['backend'] = $db_factory[$k]['backend']; // comparing $temp_result = $ddl->compare_schemas(isset($ddl->objects[$k]) ? $ddl->objects[$k] : [], isset($loaded_objects[$k]) ? $loaded_objects[$k] : [], $compare_options); if (!$temp_result['success']) { array_merge3($result['hint'], $temp_result['error']); } else { $schema_diff[$k] = $temp_result['data']; if (!isset($total_per_db_link[$k])) { $total_per_db_link[$k] = 0; } $total_per_db_link[$k] += $temp_result['count']; $total += $temp_result['count']; } } // if there's no schema changes if ($total == 0) { if ($options['mode'] == 'commit') { goto import_data; } else { $result['success'] = true; } break; } // we need to provide a list of changes foreach ($total_per_db_link as $k => $v) { $result['hint'][] = ''; $result['hint'][] = "Db link {$k} requires {$v} changes!"; // printing summary $result['hint'][] = ' * Link ' . $k . ': '; foreach ($schema_diff[$k] as $k2 => $v2) { $result['hint'][] = ' * ' . $k2 . ': '; foreach ($v2 as $k3 => $v3) { $result['hint'][] = ' * ' . $k3 . ' - ' . $v3['type']; } } } // if we are in no commit mode we exit if (!in_array($options['mode'], ['commit', 'drop'])) { break; } // generating sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $ddl_object = $db_factory[$k]['ddl_object']; foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { // we need to make fk constraints last to sort MySQL issues if ($k2 == 'new_constraints' && $v3['type'] == 'constraint_new' && $v3['data']['type'] == 'fk') { $schema_diff[$k][$k2 . '_fks'][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3); } else { $schema_diff[$k][$k2][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3, ['mode' => $options['mode']]); } } } } // print_r($schema_diff); // exit; // executing sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $db_object = new db($k); // if we are dropping we need to disable foregn key checks if ($options['mode'] == 'drop') { if ($db_object->backend == 'mysqli') { $db_object->query('SET foreign_key_checks = 0;'); // we also need to unset sequences unset($schema_diff[$k]['delete_sequences']); } } foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($v3['sql'])) { continue; } if (is_array($v3['sql'])) { $temp = $v3['sql']; } else { $temp = [$v3['sql']]; } foreach ($temp as $v4) { $temp_result = $db_object->query($v4); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); goto error; } } } } } // if we got here - we are ok $result['success'] = true; } while (0); import_data: // we need to import data if (!empty($object_import) && $options['mode'] == 'commit') { $result['hint'][] = ''; foreach ($object_import as $k => $v) { $data_object = new $k(); $data_result = $data_object->process(); if (!$data_result['success']) { throw new Exception(implode("\n", $data_result['error'])); } $result['hint'] = array_merge($result['hint'], $data_result['hint']); } } // relation if ($flag_relation && $options['mode'] == 'commit') { $result['hint'][] = ''; $model2 = factory::model('numbers_data_relations_model_relation_attributes'); // insert new models if (!empty($object_relations)) { foreach ($object_relations as $k => $v) { $result_insert = $model2->save($v, ['pk' => ['rn_relattr_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation models!'; } // we need to process forms foreach ($dep['data']['submodule_dirs'] as $v) { $dir = $v . 'model/form/'; if (!file_exists($dir)) { continue; } $files = helper_file::iterate($dir, ['only_extensions' => ['php']]); foreach ($files as $v2) { $model_name = str_replace(['../libraries/vendor/', '.php'], '', $v2); $model_name = str_replace('/', '_', $model_name); $model = new $model_name(['skip_processing' => true]); if (empty($model->form_object->misc_settings['option_models'])) { continue; } // loop though fields foreach ($model->form_object->misc_settings['option_models'] as $k3 => $v3) { $object_forms[$model_name . '::' . $k3] = ['rn_relfrmfld_form_code' => $model_name, 'rn_relfrmfld_form_name' => $model->title, 'rn_relfrmfld_field_code' => $k3, 'rn_relfrmfld_field_name' => $v3['field_name'], 'rn_relfrmfld_relattr_id' => $v3['model'], 'rn_relfrmfld_inactive' => 0]; } } } if (!empty($object_forms)) { // load all relation models $data = $model2->get(['pk' => ['rn_relattr_model']]); $model = factory::model('numbers_data_relations_model_relation_formfields'); foreach ($object_forms as $k => $v) { if (empty($data[$v['rn_relfrmfld_relattr_id']])) { continue; } $v['rn_relfrmfld_relattr_id'] = $data[$v['rn_relfrmfld_relattr_id']]['rn_relattr_id']; $result_insert = $model->save($v, ['pk' => ['rn_relfrmfld_form_code', 'rn_relfrmfld_field_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation form fields!'; } // todo: import models //print_r2($object_attributes); if (!empty($object_attributes)) { $model = factory::model('numbers_data_relations_model_attribute_models'); foreach ($object_attributes as $k => $v) { $result_insert = $model->save($v, ['pk' => ['rn_attrmdl_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported attribute models!'; } } // we need to generate documentation $system_documentation = application::get('system_documentation'); if (!empty($system_documentation) && $options['mode'] == 'commit') { $model = factory::model($system_documentation['model']); /* print_r2($object_documentation); $documentation_result = $model->update($object_documentation, $system_documentation); if (!$documentation_result['success']) { $result['error'] = array_merge($result['error'], $documentation_result['error']); } */ } error: return $result; }
/** * Compare two schemas * * @param array $obj_master * @param array $obj_slave * @return array */ public function compare_schemas($obj_master, $obj_slave, $options = []) { $result = ['success' => false, 'error' => [], 'data' => [], 'count' => 0]; // delete first $result['data']['delete_triggers'] = []; $result['data']['delete_views'] = []; $result['data']['delete_constraints'] = []; $result['data']['delete_indexes'] = []; $result['data']['delete_functions'] = []; $result['data']['delete_columns'] = []; $result['data']['delete_tables'] = []; $result['data']['delete_domains'] = []; // after tables and columns $result['data']['delete_sequences'] = []; // after domains $result['data']['delete_schemas'] = []; $result['data']['delete_extensions'] = []; // last // new second $result['data']['new_extensions'] = []; // first $result['data']['new_schemas'] = []; $result['data']['new_schema_owners'] = []; $result['data']['new_domains'] = []; // after schema $result['data']['new_domain_owners'] = []; $result['data']['new_sequences'] = []; $result['data']['new_sequence_owners'] = []; $result['data']['new_tables'] = []; $result['data']['new_table_owners'] = []; $result['data']['new_columns'] = []; $result['data']['change_columns'] = []; $result['data']['new_constraints'] = []; $result['data']['new_indexes'] = []; $result['data']['new_views'] = []; // views goes after we add columns $result['data']['change_views'] = []; $result['data']['new_view_owners'] = []; $result['data']['new_functions'] = []; $result['data']['new_function_owner'] = []; $result['data']['new_triggers'] = []; // after functions $result['data']['change_triggers'] = []; // add extension if (!empty($obj_master['extension'])) { foreach ($obj_master['extension'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_slave['extension'][$k][$k2])) { $result['data']['new_extensions'][$k . '.' . $k2] = array('type' => 'extension', 'data' => $v2); $result['count']++; } } } } // delete extensions if (!empty($obj_slave['extension'])) { foreach ($obj_slave['extension'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_master['extension'][$k][$k2])) { $result['data']['delete_extensions'][$k . '.' . $k2] = array('type' => 'extension_delete', 'data' => $v2); $result['count']++; } } } } // new schemas if (!empty($obj_master['schema'])) { foreach ($obj_master['schema'] as $k => $v) { if (empty($obj_slave['schema'][$k])) { $result['data']['new_schemas'][$k] = array('type' => 'schema', 'data' => $v); $result['count']++; } else { if ($v['owner'] != $obj_slave['schema'][$k]['owner']) { $result['data']['new_schema_owners'][$k] = array('type' => 'schema_owner', 'data' => $v); $result['count']++; } } } } // delete schema if (!empty($obj_slave['schema'])) { foreach ($obj_slave['schema'] as $k => $v) { if (empty($obj_master['schema'][$k])) { $result['data']['delete_schemas'][$k] = array('type' => 'schema_delete', 'data' => $v); $result['count']++; } } } // new tables if (!empty($obj_master['table'])) { foreach ($obj_master['table'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_slave['table'][$k][$k2])) { $result['data']['new_tables'][$v2['full_table_name']] = array('type' => 'table_new', 'data' => $v2); $result['count']++; } else { if ($v2['owner'] != $obj_slave['table'][$k][$k2]['owner']) { $result['data']['new_table_owners'][$v2['full_table_name']] = array('type' => 'table_owner', 'data' => $v2); $result['count']++; } } } } } // delete table if (isset($obj_slave['table'])) { foreach ($obj_slave['table'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_master['table'][$k][$k2])) { $result['data']['delete_tables'][$v2['full_table_name']] = array('type' => 'table_delete', 'data' => $v2); $result['count']++; } } } } // new columns if (!empty($obj_master['table'])) { foreach ($obj_master['table'] as $k => $v) { foreach ($v as $k2 => $v2) { // if we have new table we do not need to check for new columns if (!empty($result['data']['new_tables'][$v2['full_table_name']])) { continue; } // finding new column foreach ($v2['columns'] as $k3 => $v3) { if (empty($obj_slave['table'][$k][$k2]['columns'][$k3])) { $result['data']['new_columns'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'column_new', 'name' => $k3, 'table' => $v2['full_table_name'], 'data' => $v3); $result['count']++; } else { // comparing data types $temp_error = false; if ($v3['type'] != $obj_slave['table'][$k][$k2]['columns'][$k3]['type']) { if (strpos($v3['type'], 'serial') !== false || strpos($obj_slave['table'][$k][$k2]['columns'][$k3]['type'], 'serial') !== false) { $result['error'][] = 'Serial data type changes must be handled manually, column ' . $k . '.' . $k2 . '.' . $k3; return $result; } $temp_error = true; } if (!isset($v3['null'])) { $v3['null'] = false; } if (!isset($obj_slave['table'][$k][$k2]['columns'][$k3]['null'])) { $obj_slave['table'][$k][$k2]['columns'][$k3]['null'] = false; } if ($v3['null'] != $obj_slave['table'][$k][$k2]['columns'][$k3]['null']) { $temp_error = true; } if (!isset($v3['default'])) { $v3['default'] = null; } if (!isset($obj_slave['table'][$k][$k2]['columns'][$k3]['default'])) { $obj_slave['table'][$k][$k2]['columns'][$k3]['default'] = null; } if ($v3['default'] != $obj_slave['table'][$k][$k2]['columns'][$k3]['default']) { $temp_error = true; } // auto_increment for mysqli if ($options['backend'] == 'mysqli') { if (!isset($v3['auto_increment'])) { $v3['auto_increment'] = 0; } if ($v3['auto_increment'] != $obj_slave['table'][$k][$k2]['columns'][$k3]['auto_increment']) { $temp_error = true; } } if ($temp_error) { $result['data']['change_columns'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'column_change', 'name' => $k3, 'table' => $v2['full_table_name'], 'data' => $v3, 'data_slave' => $obj_slave['table'][$k][$k2]['columns'][$k3]); $result['count']++; } } } // finding columns to be deleted foreach ($obj_slave['table'][$k][$k2]['columns'] as $k3 => $v3) { if (empty($obj_master['table'][$k][$k2]['columns'][$k3])) { $result['data']['delete_columns'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'column_delete', 'name' => $k3, 'table' => $v2['full_table_name']); $result['count']++; } } } } } // exceptions for mysqli submodule if ($options['backend'] == 'mysqli' && !empty($obj_slave['constraint'])) { foreach ($obj_slave['constraint'] as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if ($v3['type'] == 'pk') { $found = null; if (!empty($obj_master['constraint'][$k][$k2])) { foreach ($obj_master['constraint'][$k][$k2] as $k4 => $v4) { if ($v4['type'] == 'pk') { $found = $k4; break; } } } if (!empty($found)) { $temp = $obj_slave['constraint'][$k][$k2][$k3]; unset($obj_slave['constraint'][$k][$k2][$k3]); $obj_slave['constraint'][$k][$k2][$found] = $temp; } } } } } } // new constraints if (!empty($obj_master['constraint'])) { foreach ($obj_master['constraint'] as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($obj_slave['constraint'][$k][$k2][$k3])) { $result['data']['new_constraints'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'constraint_new', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count']++; } else { // comparing structure $temp_error = false; if ($v3['type'] != $obj_slave['constraint'][$k][$k2][$k3]['type']) { $temp_error = true; } if ($v3['full_table_name'] != $obj_slave['constraint'][$k][$k2][$k3]['full_table_name']) { $temp_error = true; } if (!array_compare_level1($v3['columns'], $obj_slave['constraint'][$k][$k2][$k3]['columns'])) { $temp_error = true; } // additiona verifications for fk constraints if ($v3['type'] == 'fk') { if ($v3['foreign_table'] != $obj_slave['constraint'][$k][$k2][$k3]['foreign_table']) { $temp_error = true; } if (!array_compare_level1($v3['foreign_columns'], $obj_slave['constraint'][$k][$k2][$k3]['foreign_columns'])) { $temp_error = true; } } // if we have an error we rebuild if ($temp_error) { $result['data']['delete_constraints'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'constraint_delete', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['data']['new_constraints'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'constraint_new', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count'] += 1; } } } } } } // delete constraints if (isset($obj_slave['constraint'])) { foreach ($obj_slave['constraint'] as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($obj_master['constraint'][$k][$k2][$k3])) { $result['data']['delete_constraints'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'constraint_delete', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count']++; } } } } } // new indexes if (!empty($obj_master['index'])) { foreach ($obj_master['index'] as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($obj_slave['index'][$k][$k2][$k3])) { $result['data']['new_indexes'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'index_new', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count']++; } else { // comparing structure $temp_error = false; if ($v3['type'] != $obj_slave['index'][$k][$k2][$k3]['type']) { $temp_error = true; } if (!array_compare_level1($v3['columns'], $obj_slave['index'][$k][$k2][$k3]['columns'])) { $temp_error = true; } // todo: comparison for foreign key & check if ($temp_error) { $result['data']['delete_indexes'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'index_delete', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $obj_slave['index'][$k][$k2][$k3]); $result['data']['new_indexes'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'index_new', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count'] += 1; } } } } } } // delete indexes if (isset($obj_slave['index'])) { foreach ($obj_slave['index'] as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($obj_master['index'][$k][$k2][$k3])) { $result['data']['delete_indexes'][$k . '.' . $k2 . '.' . $k3] = array('type' => 'index_delete', 'name' => $k3, 'table' => $v3['full_table_name'], 'data' => $v3); $result['count']++; } } } } } // for mysqli/pgsql we need to put new sequences after new tables if ($options['backend'] == 'mysqli' || $options['backend'] == 'pgsql') { unset($result['data']['new_sequences']); $result['data']['new_sequences'] = []; } // new sequences if (!empty($obj_master['sequence'])) { foreach ($obj_master['sequence'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_slave['sequence'][$k][$k2])) { $result['data']['new_sequences'][$k . '.' . $k2] = array('type' => 'sequences_new', 'name' => $v2['full_sequence_name'], 'owner' => $v2['owner'], 'data' => $v2); $result['count']++; } else { if ($options['backend'] == 'pgsql') { // todo: fix here for pgsql!!! // checking owner information if ($v2['owner'] != $obj_slave['sequence'][$k][$k2]['owner']) { $result['data']['new_sequence_owners'][$k . '.' . $k2] = array('type' => 'sequence_owner', 'name' => $v2['full_sequence_name'], 'owner' => $v2['owner'], 'data' => $v2); } } } } } } // delete sequences if (!empty($obj_slave['sequence'])) { foreach ($obj_slave['sequence'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_master['sequence'][$k][$k2])) { $result['data']['delete_sequences'][$k . '.' . $k2] = array('type' => 'sequence_delete', 'name' => $v2['full_sequence_name'], 'data' => $v2); $result['count']++; } } } } // functions if (!empty($obj_master['function'])) { foreach ($obj_master['function'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_slave['function'][$k][$k2])) { $result['data']['new_functions'][$k . '.' . $k2] = array('type' => 'function_new', 'name' => $v2['full_function_name'], 'owner' => $v2['owner'], 'data' => $v2); $result['count']++; } else { // if function has changed if ($options['backend'] == 'mysqli') { if ($v2['sql_parts']['body'] != $obj_slave['function'][$k][$k2]['sql_parts']['body']) { $result['data']['delete_functions'][$k . '.' . $k2] = array('type' => 'function_delete', 'name' => $v2['full_function_name'], 'data' => $v2); $result['data']['new_functions'][$k . '.' . $k2] = array('type' => 'function_new', 'name' => $v2['full_function_name'], 'owner' => $v2['owner'], 'data' => $v2); $result['count']++; } } else { if ($options['backend'] == 'pgsql') { if (numbers_backend_db_class_ddl::sanitize_function($v2['sql_full']) != numbers_backend_db_class_ddl::sanitize_function($obj_slave['function'][$k][$k2]['sql_full'])) { $result['data']['delete_functions'][$k . '.' . $k2] = array('type' => 'function_delete', 'name' => $v2['full_function_name'], 'data' => $v2); $result['data']['new_functions'][$k . '.' . $k2] = array('type' => 'function_new', 'name' => $v2['full_function_name'], 'owner' => $v2['owner'], 'data' => $v2); $result['count']++; } else { if ($v2['owner'] != $obj_slave['function'][$k][$k2]['owner']) { // checking owner $result['data']['new_function_owners'][$k . '.' . $k2] = array('type' => 'function_owner', 'name' => $v2['full_function_name'], 'owner' => $v2['owner'], 'data' => $v2); $result['count']++; } } } } } } } } // delete function if (!empty($obj_slave['function'])) { foreach ($obj_slave['function'] as $k => $v) { foreach ($v as $k2 => $v2) { if (empty($obj_master['function'][$k][$k2])) { $result['data']['delete_functions'][$k . '.' . $k2] = array('type' => 'function_delete', 'name' => $v2['full_function_name'], 'data' => $v2); $result['count']++; } } } } // if we delete tables there's no need to delete constrants and/or indexes foreach ($result['data']['delete_tables'] as $k => $v) { // unsetting constraints foreach ($result['data']['delete_constraints'] as $k2 => $v2) { if ($v2['data']['full_table_name'] == $k) { unset($result['data']['delete_constraints'][$k2]); } } // unsetting indexes foreach ($result['data']['delete_indexes'] as $k2 => $v2) { if ($v2['data']['full_table_name'] == $k) { unset($result['data']['delete_indexes'][$k2]); } } } // final step clean up empty keys foreach ($result['data'] as $k => $v) { // clean up empty nodes if (empty($v)) { unset($result['data'][$k]); } } $result['success'] = true; return $result; }