/** * Invoke method, every class will have its own * returns true/false on completion, setting both * errormsg and output as necessary */ function invoke() { parent::invoke(); $result = true; /// Set own core attributes $this->does_generate = ACTION_NONE; //$this->does_generate = ACTION_GENERATE_HTML; /// These are always here global $CFG, $XMLDB; /// Do the job, setting $result as needed /// Get the dir containing the file $dirpath = required_param('dir', PARAM_PATH); $dirpath = $CFG->dirroot . $dirpath; /// Get the correct dir if (!empty($XMLDB->dbdirs)) { $dbdir =& $XMLDB->dbdirs[$dirpath]; if ($dbdir) { /// Set some defaults $dbdir->xml_exists = false; $dbdir->xml_writeable = false; $dbdir->xml_loaded = false; ///Only if the directory exists if (!$dbdir->path_exists) { return false; } $xmldb_file = new xmldb_file($dbdir->path . '/install.xml'); ///Set the XML DTD and schema $xmldb_file->setDTD($CFG->dirroot . '/lib/xmldb/xmldb.dtd'); $xmldb_file->setSchema($CFG->dirroot . '/lib/xmldb/xmldb.xsd'); /// Set dbdir as necessary if ($xmldb_file->fileExists()) { $dbdir->xml_exists = true; } if ($xmldb_file->fileWriteable()) { $dbdir->xml_writeable = true; } /// Load the XML contents to structure $loaded = $xmldb_file->loadXMLStructure(); if ($loaded && $xmldb_file->isLoaded()) { $dbdir->xml_loaded = true; $dbdir->filemtime = filemtime($dbdir->path . '/install.xml'); } $dbdir->xml_file = $xmldb_file; } else { $this->errormsg = 'Wrong directory (' . $dirpath . ')'; $result = false; } } else { $this->errormsg = 'XMLDB structure not found'; $result = false; } /// Launch postaction if exists if ($this->getPostAction() && $result) { return $this->launch($this->getPostAction()); } return $result; }
/** * Returns names of all known tables == tables that moodle knowns about. * @return array of lowercase table names */ function get_used_table_names() { $table_names = array(); $dbdirs = get_db_directories(); foreach ($dbdirs as $dbdir) { $file = $dbdir . '/install.xml'; $xmldb_file = new xmldb_file($file); if (!$xmldb_file->fileExists()) { continue; } $loaded = $xmldb_file->loadXMLStructure(); $structure = $xmldb_file->getStructure(); if ($loaded and $tables = $structure->getTables()) { foreach ($tables as $table) { $table_names[] = strtolower($table->name); } } } return $table_names; }
/** * Invoke method, every class will have its own * returns true/false on completion, setting both * errormsg and output as necessary */ function invoke() { parent::invoke(); $result = true; /// Set own core attributes $this->does_generate = ACTION_NONE; //$this->does_generate = ACTION_GENERATE_HTML; /// These are always here global $CFG, $XMLDB; /// Do the job, setting $result as needed /// Iterate over $XMLDB->dbdirs, loading their XML data to memory if ($XMLDB->dbdirs) { $dbdirs =& $XMLDB->dbdirs; foreach ($dbdirs as $dbdir) { /// Set some defaults $dbdir->xml_exists = false; $dbdir->xml_writeable = false; $dbdir->xml_loaded = false; ///Only if the directory exists if (!$dbdir->path_exists) { continue; } $xmldb_file = new xmldb_file($dbdir->path . '/install.xml'); /// Set dbdir as necessary if ($xmldb_file->fileExists()) { $dbdir->xml_exists = true; } if ($xmldb_file->fileWriteable()) { $dbdir->xml_writeable = true; } /// Load the XML contents to structure $loaded = $xmldb_file->loadXMLStructure(); if ($loaded && $xmldb_file->isLoaded()) { $dbdir->xml_loaded = true; } $dbdir->xml_file = $xmldb_file; } } return $result; }
private function recreate_table($component, $tablename) { global $DB; unset($DB->donesetup); $manager = $DB->get_manager(); $filename = get_component_directory($component) . "/db/install.xml"; $xmldbfile = new xmldb_file($filename); if (!$xmldbfile->fileExists()) { throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); } $loaded = $xmldbfile->loadXMLStructure(); if (!$loaded || !$xmldbfile->isLoaded()) { // Show info about the error if we can find it. if ($structure =& $xmldbfile->getStructure()) { if ($errors = $structure->getAllErrors()) { throw new ddl_exception('ddlxmlfileerror', null, 'Errors found in XMLDB file: ' . implode(', ', $errors)); } } throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); } $structure = $xmldbfile->getStructure(); $table = $structure->getTable($tablename); $manager->create_table($table); $DB->donesetup = true; }
/** * Create temporary tables to speed up log generation */ function stats_temp_table_create() { global $CFG, $DB; $dbman = $DB->get_manager(); // We are going to use database_manager services stats_temp_table_drop(); $xmlfile = $CFG->dirroot . '/lib/db/install.xml'; $tables = array(); // Allows for the additional xml files to be used (if necessary) $files = array($xmlfile => array('stats_daily' => array('temp_stats_daily'), 'stats_user_daily' => array('temp_stats_user_daily'), 'temp_enroled_template' => array('temp_enroled'), 'temp_log_template' => array('temp_log1', 'temp_log2'))); foreach ($files as $file => $contents) { $xmldb_file = new xmldb_file($file); if (!$xmldb_file->fileExists()) { throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); } $loaded = $xmldb_file->loadXMLStructure(); if (!$loaded || !$xmldb_file->isLoaded()) { throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); } $xmldb_structure = $xmldb_file->getStructure(); foreach ($contents as $template => $names) { $table = $xmldb_structure->getTable($template); if (is_null($table)) { throw new ddl_exception('ddlunknowntable', null, 'The table ' . $name . ' is not defined in the file ' . $xmlfile); } $table->setNext(null); $table->setPrevious(null); foreach ($names as $name) { $named = clone $table; $named->setName($name); $tables[$name] = $named; } } } try { foreach ($tables as $table) { $dbman->create_temp_table($table); } } catch (Exception $e) { mtrace('Temporary table creation failed: ' . $e->getMessage()); return false; } return true; }
/** * Invoke method, every class will have its own * returns true/false on completion, setting both * errormsg and output as necessary */ function invoke() { parent::invoke(); $result = true; /// Set own core attributes $this->does_generate = ACTION_GENERATE_HTML; /// These are always here global $CFG, $XMLDB, $DB, $OUTPUT; /// And we nedd some ddl suff $dbman = $DB->get_manager(); /// Here we'll acummulate all the wrong fields found $problemsfound = array(); /// Do the job, setting $result as needed /// Get the confirmed to decide what to do $confirmed = optional_param('confirmed', false, PARAM_BOOL); /// If not confirmed, show confirmation box if (!$confirmed) { $o = '<table class="generalbox" border="0" cellpadding="5" cellspacing="0" id="notice">'; $o .= ' <tr><td class="generalboxcontent">'; $o .= ' <p class="centerpara">' . $this->str[$this->introstr] . '</p>'; $o .= ' <table class="boxaligncenter" cellpadding="20"><tr><td>'; $o .= ' <div class="singlebutton">'; $o .= ' <form action="index.php?action=' . $this->title . '&confirmed=yes&sesskey=' . sesskey() . '" method="post"><fieldset class="invisiblefieldset">'; $o .= ' <input type="submit" value="' . $this->str['yes'] . '" /></fieldset></form></div>'; $o .= ' </td><td>'; $o .= ' <div class="singlebutton">'; $o .= ' <form action="index.php?action=main_view" method="post"><fieldset class="invisiblefieldset">'; $o .= ' <input type="submit" value="' . $this->str['no'] . '" /></fieldset></form></div>'; $o .= ' </td></tr>'; $o .= ' </table>'; $o .= ' </td></tr>'; $o .= '</table>'; $this->output = $o; } else { /// The back to edit table button $b = ' <p class="centerpara buttons">'; $b .= '<a href="index.php">[' . $this->str['back'] . ']</a>'; $b .= '</p>'; /// Iterate over $XMLDB->dbdirs, loading their XML data to memory if ($XMLDB->dbdirs) { $dbdirs =& $XMLDB->dbdirs; $o = '<ul>'; foreach ($dbdirs as $dbdir) { /// Only if the directory exists if (!$dbdir->path_exists) { continue; } /// Load the XML file $xmldb_file = new xmldb_file($dbdir->path . '/install.xml'); /// Only if the file exists if (!$xmldb_file->fileExists()) { continue; } /// Load the XML contents to structure $loaded = $xmldb_file->loadXMLStructure(); if (!$loaded || !$xmldb_file->isLoaded()) { echo $OUTPUT->notification('Errors found in XMLDB file: ' . $dbdir->path . '/install.xml'); continue; } /// Arriving here, everything is ok, get the XMLDB structure $structure = $xmldb_file->getStructure(); $o .= ' <li>' . str_replace($CFG->dirroot . '/', '', $dbdir->path . '/install.xml'); /// Getting tables if ($xmldb_tables = $structure->getTables()) { $o .= ' <ul>'; /// Foreach table, process its fields foreach ($xmldb_tables as $xmldb_table) { /// Skip table if not exists if (!$dbman->table_exists($xmldb_table)) { continue; } /// Fetch metadata from physical DB. All the columns info. if (!($metacolumns = $DB->get_columns($xmldb_table->getName()))) { //// Skip table if no metacolumns is available for it continue; } /// Table processing starts here $o .= ' <li>' . $xmldb_table->getName(); /// Do the specific check. list($output, $newproblems) = $this->check_table($xmldb_table, $metacolumns); $o .= $output; $problemsfound = array_merge($problemsfound, $newproblems); $o .= ' </li>'; /// Give the script some more time (resetting to current if exists) if ($currenttl = @ini_get('max_execution_time')) { @ini_set('max_execution_time', $currenttl); } } $o .= ' </ul>'; } $o .= ' </li>'; } $o .= '</ul>'; } /// Create a report of the problems found. $r = $this->display_results($problemsfound); /// Combine the various bits of output. $this->output = $b . $r . $o; } /// Launch postaction if exists (leave this here!) if ($this->getPostAction() && $result) { return $this->launch($this->getPostAction()); } /// Return ok if arrived here return $result; }
/** * Given one "real" tablename, create one temp table suitable for be used in backup/restore operations */ public static function create_temptable_from_real_table($backupid, $realtablename, $temptablename) { global $CFG, $DB; $dbman = $DB->get_manager(); // We are going to use database_manager services // As far as xmldb objects use a lot of circular references (prev and next) and we aren't destroying // them at all, that causes one memory leak of about 3M per backup execution, not problematic for // individual backups but critical for automated (multiple) ones. // So we are statically caching the xmldb_table definition here to produce the leak "only" once static $xmldb_tables = array(); // Not cached, get it if (!isset($xmldb_tables[$realtablename])) { // Note: For now we are going to load the realtablename from core lib/db/install.xml // that way, any change in the "template" will be applied here automatically. If this causes // too much slow, we can always forget about the template and keep maintained the xmldb_table // structure inline - manually - here. // TODO: Right now, loading the whole lib/db/install.xml is "eating" 10M, we should // change our way here in order to decrease that memory usage $templatetablename = $realtablename; $targettablename = $temptablename; $xmlfile = $CFG->dirroot . '/lib/db/install.xml'; $xmldb_file = new xmldb_file($xmlfile); if (!$xmldb_file->fileExists()) { throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); } $loaded = $xmldb_file->loadXMLStructure(); if (!$loaded || !$xmldb_file->isLoaded()) { throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); } $xmldb_structure = $xmldb_file->getStructure(); $xmldb_table = $xmldb_structure->getTable($templatetablename); if (is_null($xmldb_table)) { throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $xmlfile); } // Clean prev & next, we are alone $xmldb_table->setNext(null); $xmldb_table->setPrevious(null); // Rename $xmldb_table->setName($targettablename); // Cache it $xmldb_tables[$realtablename] = $xmldb_table; } // Arrived here, we have the table always in static cache, get it $xmldb_table = $xmldb_tables[$realtablename]; // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action! $xmldb_table->getField('backupid')->setDefault($backupid); $dbman->create_temp_table($xmldb_table); // And create it }
/** * Reads the install.xml files for Moodle core and modules and returns an array of * xmldb_structure object with xmldb_table from these files. * @return xmldb_structure schema from install.xml files */ public function get_install_xml_schema() { global $CFG; require_once $CFG->libdir . '/adminlib.php'; $schema = new xmldb_structure('export'); $schema->setVersion($CFG->version); $dbdirs = get_db_directories(); foreach ($dbdirs as $dbdir) { $xmldb_file = new xmldb_file($dbdir . '/install.xml'); if (!$xmldb_file->fileExists() or !$xmldb_file->loadXMLStructure()) { continue; } $structure = $xmldb_file->getStructure(); $tables = $structure->getTables(); foreach ($tables as $table) { $table->setPrevious(null); $table->setNext(null); $schema->addTable($table); } } return $schema; }
/** * Finds all of the XMLDB files within a given plugin path and sets up the overlay table array to include * the tables defined within those plugins. * * @param string $path The path to look for modules in * @return array An array of extra overlay tables */ protected static function load_plugin_xmldb($path) { global $CFG; require_once $CFG->libdir . '/ddllib.php'; $tables = array(); switch ($path) { case 'mod': $prefix = 'mod_'; break; case 'course/format': $prefix = 'format_'; break; default: return array(); } $plugins = get_list_of_plugins($path); if ($plugins) { foreach ($plugins as $plugin) { if (!file_exists($CFG->dirroot . '/' . $path . '/' . $plugin . '/db/install.xml')) { continue; } // Load the XMLDB file and pull the tables out of the XML strcture. $xmldbfile = new xmldb_file($CFG->dirroot . '/' . $path . '/' . $plugin . '/db/install.xml'); if (!$xmldbfile->fileExists()) { continue; } $xmldbfile->loadXMLStructure(); $xmldbstructure = $xmldbfile->getStructure(); $xmldbtables = $xmldbstructure->getTables(); if (!empty($xmldbtables)) { foreach ($xmldbtables as $xmldbtable) { // Add each table to the list of overlay tables. $tables[$xmldbtable->getName()] = $prefix . $plugin; } } } } return $tables; }
function cleanup() { global $DB; $dbman = $DB->get_manager(); $tablename = student::TABLE; $tempname = $tablename . '_temp'; $table = new xmldb_table($tempname); if ($dbman->table_exists($table)) { if (!$dbman->drop_table($table)) { mtrace(' <<< Could not remove temporary table: ' . $tempname); exit; } } // Create a temporary table by reading in the XMLDB file that defines the student enrolment table $xmldb_file = new xmldb_file(elispm::file('db/install.xml')); if (!$xmldb_file->fileExists() or !$xmldb_file->loadXMLStructure()) { continue; } $structure = $xmldb_file->getStructure(); $tables = $structure->getTables(); foreach ($tables as $table) { if ($table->getName() == $tablename) { $xml_temptable = $table; $xml_temptable->setName($tempname); $xml_temptable->setPrevious(null); $xml_temptable->setNext(null); $tempstructure = new xmldb_structure('temp'); $tempstructure->addTable($xml_temptable); try { $dbman->install_from_xmldb_structure($tempstructure); } catch (ddl_change_structure_exception $e) { mtrace(' <<< Could not create temporary table: ' . $tempname); exit; } mtrace(' >>> Created temporary table: ' . $tempname); } } $xml_table = new xmldb_table($tablename); // Step 1. -- attempt to move unique values into the temporary table in a way that should leave some duplicates but // will remove the vast majority of the them $sql = "INSERT INTO {{$tempname}}\n SELECT id, classid, userid, enrolmenttime, MIN(completetime) AS completetime, endtime, completestatusid,\n grade, credits, locked\n FROM {{$tablename}}\n GROUP BY classid, userid, completestatusid, grade, credits, locked"; try { $DB->execute($sql); } catch (dml_exception $e) { mtrace(' <<< Could not move duplicate records from: ' . $tablename . ' into: ' . $tempname); exit; } mtrace(' >>> Moved duplicate records from: ' . $tablename . ' into: ' . $tempname); // Step 2. -- detect if we still have any duplicates remaining $sql = "SELECT id, COUNT(*) AS count, classid, userid, enrolmenttime, completetime, completestatusid, grade, credits, locked\n FROM {{$tempname}}\n GROUP BY classid, userid\n ORDER BY count DESC, classid ASC, userid ASC"; if ($dupcount = $DB->get_records_sql($sql, array(), 0, 1)) { $dupe = current($dupcount); if ($dupe->count > 1) { mtrace(' <<< Duplicate records still exist in temporary table'); } else { mtrace(' >>> No duplicate records exist in temporary table'); } } // Step 3. -- at this point duplicate data was found, so we will need to process each record in turn to find the first // legitimate record that should be kept if ($rs = $DB->get_recordset_sql($sql)) { foreach ($rs as $dupe) { if ($dupe->count <= 1) { continue; } $classid = $dupe->classid; $userid = $dupe->userid; $goodid = 0; // The ID of the record we will keep // Find the record marked "complete" or "failed" and locked with the earliest completion time $sql2 = "SELECT id, completestatusid, grade locked\n FROM {{$tempname}}\n WHERE userid = {$userid}\n AND classid = {$classid}\n ORDER BY completetime ASC, completestatusid ASC, locked ASC"; if ($rs2 = $DB->get_recordset_sql($sql2)) { foreach ($rs2 as $rec) { // Store the last record ID just in case we need it for cleanup $lastid = $rec->id; // Don't bother looking at remaining records if we have found a record to keep if (!empty($goodid)) { continue; } if ($rec->completestatusid != 0 && ($rec->locked = 1)) { $goodid = $rec->id; } } $rs2->close(); // We need to make sure we have a record ID to keep, if we found no "complete" and locked // records, let's just keep the last record we saw if (empty($goodid)) { $goodid = $lastid; } $select = 'classid = ' . $classid . ' AND userid = ' . $userid . ' AND id != ' . $goodid; } // If we have some records to clean up, let's do that now if (!empty($select)) { $status = true; try { $DB->delete_records_select($tempname, $select); } catch (dml_exception $e) { mtrace(' <<< Could not clean up duplicate ' . $tempname . ' records for userid = ' . $userid . ', classid = ' . $classid); $status = false; } if ($status) { mtrace(' >>> Cleaned up duplicate ' . $tempname . ' records for userid = ' . $userid . ', classid = ' . $classid); } } } $rs->close(); } // Step 4. -- drop the table containing duplicate values and rename the temporary table to take it's place try { $dbman->drop_table($xml_table); } catch (ddl_change_structure_exception $e) { mtrace(' <<< Could not drop table: ' . $tablename); exit; } mtrace(' >>> Successfully dropped table: ' . $tablename); // Rename the temporary table to allow it to replace the real table try { $dbman->rename_table($xml_temptable, $tablename); } catch (ddl_change_structure_exception $e) { mtrace(' <<< Could not rename table: ' . $tempname . ' to: ' . $tablename); exit; } mtrace(' >>> Successfully renamed table: ' . $tempname . ' to: ' . $tablename); }