run (); /// Controller /// /// Manages the view and the Model. /// class Controller { public $isConnectedToDb = false; protected $_connInfo; protected $_vw; protected $_m; protected $_whatToDump; protected $_dropFirst; protected $_onlyNewTables; protected $_clearNonData; protected $_f = NULL; protected $_sql; protected $_showWarnings; const kDefaultCharsetForDbModel = 'utf8'; const kDefaultCollationForDbModel = 'utf8_general_ci'; public function _destruct () { $this->_f && fclose ($this->_f); } // Run the App. public function run () { $vw = $this->_vw; $op = getQSStr ('op'); $paramSrc =& $_POST; // Decide what to do. $meth = ''; switch ($op) { case 'dump': $vw = new HTMLView_Dump ($this); $meth = '_dump'; break; case 'load': $vw = new HTMLView_Load ($this); $meth = '_load'; break; case 'query': $vw = new HTMLView_Query ($this); $meth = '_query'; break; default: // Or we show the welcome page or we're in a popup and are expected to show the table list for selection. if (getQSStr ('pop') === 'seltables') { $vw = new HTMLView_ShowTableList ($this); $meth= '_showTableList'; $paramSrc =& $_GET; } else { $vw = new HTMLView_Welcome ($this); $vw->render (); return; } } $this->_vw = $vw; // Read input about database connection info; $this->_connInfo = $connInfo = new stdclass; $connInfo->server = getAStr ($paramSrc, 'txtServer'); $connInfo->portNum = getAStr ($paramSrc, 'txtPortNum'); $connInfo->user = getAStr ($paramSrc, 'txtUserName'); $connInfo->pwd = getAStr ($paramSrc, 'txtPwd'); $connInfo->dbName = getAStr ($paramSrc, 'txtDb'); // Create the model. We have to be aware that it may throw exceptions if we supply invalid data. if ($paramSrc) { // Main processing. try { $this->_m = $m = new DbModel ($connInfo, new ObserverWeb ($vw, LOG_SUCC_STMTS_FILE, LOG_PEND_STMTS_FILE, LOG_FAILED_STMTS_FILE)); $this->isConnectedToDb = true; } catch (DbConnException $e) { $vw->ackError ($e->getMessage ()); $this->_m = NULL; } } else { $connInfo->server = 'localhost'; $connInfo->portNum = 3306; $connInfo->user = 'root'; } try { call_user_func_array (array ($this, $meth), NULL); } catch (DbModelHalt $e) {} } // Called by the view for doing the dumping process. public function vw_dump () { if (!($m = $this->_m)) return; $m->observer->msg (EXPORT_NOTICE); try { $m->dump ($this->_whatToDump, $this->_dropFirst, $this->_onlyNewTables) && ($this->_vw->processFinishedMsg = 'Finished Ok'); } catch (Exception $e) { $vw->ackError ($e->getMessage ()); } } // Called by the view for doing the loading process. public function vw_load () { if (!($m = $this->_m)) return; $vw = $this->_vw; try { if ($this->_clearNonData) { $vw->ackNotification ('Clearing non-data...'); $m->clearNonData (); } if ($this->_clearConstraints) { $vw->ackNotification ('Clearing constraints...'); $m->clearConstraints (); } $vw->ackNotification ('Loading... Please wait'); $onEachSqlStmt = '_execLoadingStmt'; $m->startLoading (); $res = $this->_f ? $this->_loadFromFile ($this->_f, $onEachSqlStmt) : $this->_loadFromText ($this->_sql, $onEachSqlStmt); $m->endLoading (); $m->errorCount < 1 && ($vw->processFinishedMsg = 'Finished Ok'); // Remove the script, if that's what's instructed. ($m->errorCount < 1) && $this->_selfDestruct && !DEBUGGING && @unlink ($_SERVER ['SCRIPT_FILENAME']); } catch (Exception $e) { $m->abortLoading (); $vw->ackError ($e->getMessage ()); } } // Called by the view for doing the querying of the query page. public function vw_query () { if (!($m = $this->_m) || !($sql = $this->_sql)) return; $vw = $this->_vw; $vw->processFinishedMsg = 'Done'; try { $this->_loadFromText ($sql, '_queryAndPrint'); } catch (Exception $e) { $vw->ackError ($e->getMessage ()); } } // Dump the data. protected function _dump () { $m = $this->_m; $vw = $this->_vw; // Read input. $deftCharset = getPostStr ('txtDeftCharset', self::kDefaultCharsetForDbModel); $deftCollation = getPostStr ('txtDeftCollation', self::kDefaultCollationForDbModel); $tables = getPostStr ('txtTables'); $this->_whatToDump = $whatToDump = intval (getPostStr ('cmbWhatToDump')); $this->_dropFirst = $dropFirst = !getPostStr ('chkNoDrop'); $this->_onlyNewTables = $onlyNewTables = !!getPostStr ('chkOnlyNewTables'); $fn = getPostStr ('txtFileName'); $saveFile = !!getPostStr ('chkSaveToFile'); if ($m) try { // Validate. if ($saveFile && !$fn) throw new ValidationException ('You must enter the name of the file to create'); // Configure the Model. $saveFile && ($m->observer = new ObserverFile ($vw, LOG_SUCC_STMTS_FILE, LOG_PEND_STMTS_FILE, LOG_FAILED_STMTS_FILE, $fn)); $m->setCharset ($deftCharset, $deftCollation); $m->dumpingTablesFilter = $this->_splitTableList ($tables); } catch (Exception $e) { $vw->ackError ($e->getMessage ()); $this->_m = $m = NULL; } // Show the view. $vw->title = $this->isConnectedToDb ? "Dumping data from \"{$this->_connInfo->dbName}\"" : 'Dump data'; $vw->render ($this->_connInfo, $deftCharset, $deftCollation, $tables, $whatToDump, $dropFirst, $onlyNewTables, $fn, $saveFile); return true; } // Load the data. protected function _load () { $m = $this->_m; $vw = $this->_vw; // Read input. $this->_sql = $sql = getPostStr ('txtSQL'); $this->_selfDestruct = $selfDestruct = sizeof ($_POST) ? !!getPostStr ('chkSelfDestruct') : true; $this->_clearNonData = $clearNonData = !!getPostStr ('chkClearNonData'); $this->_clearConstraints = $clearConstraints = !!getPostStr ('chkClearConstraints'); $fl = getPostStr ('txtFileName'); $haltOnError = !!getPostStr ('chkHaltOnError'); $tmpfn = @$_FILES ['txtUplFile']['tmp_name']; $isUpl = is_uploaded_file ($tmpfn); if ($m) try { // Validate. if (!$fl && !$sql && !$isUpl) throw new ValidationException ('Enter the file or the SQL to execute'); $c = 0; $fl && ++$c; $sql && ++$c; $isUpl && ++$c; if ($c > 1) throw new ValidationException ('You can\'t enter more than one source for loading'); // Try opening the file. if (!$sql) { $fn = $fl ? $fl : $tmpfn; if (!($f = @fopen ($fn, 'r'))) throw new ValidationException ("Couldn't open \"$fn\""); $this->_f = $f; } // Configure the Model. $m->haltOnError = $haltOnError; } catch (Exception $e) { $vw->ackError ($e->getMessage ()); $this->_m = $m = NULL; } // Show the view. $vw->title = $this->isConnectedToDb ? "Loading data to \"{$this->_connInfo->dbName}\"" : 'Load data'; $vw->render ($this->_connInfo, $fl, $sql, $selfDestruct, $haltOnError, $clearNonData, $clearConstraints); return true; } // Allow querying as if there's no tomorrow. protected function _query () { $m = $this->_m; $vw = $this->_vw; // Read input. $this->_sql = $sql = getPostStr ('txtSQL'); $charset = getPostStr ('txtCharset', self::kDefaultCharsetForDbModel); $collation = getPostStr ('txtCollation', self::kDefaultCollationForDbModel); $haltOnError = !!getPostStr ('chkHaltOnError'); $this->_showWarnings = $showWarnings = $_POST ? !!getPostStr ('chkShowWarnings') : 1; // Validate. if ($_POST && !$sql) { $vw->ackError ('Please enter some SQL bellow'); $this->_m = $m = NULL; } // Configure the Model. if ($m) { $m->setCharset ($charset, $collation); $m->haltOnError = $haltOnError; } // Show the view. $vw->title = $this->isConnectedToDb ? "Query \"{$this->_connInfo->dbName}\" at will" : 'Query at will'; $vw->render ($this->_connInfo, $sql, $charset, $collation, $haltOnError, $showWarnings); } // Creates the table list page. protected function _showTableList () { $vw = $this->_vw; // List tables. $tbllst = NULL; $qchar = ''; if ($m = $this->_m) { $tbllst = $m->getTableNames (); $qchar = $m->quotingChar; } // Show the view. $vw->title = 'Select tables for dumping'; $vw->render ($tbllst, $qchar); } // Split TXT and return an array with table names. They must be separated with commas and may be quoted. protected function _splitTableList ($txt) { $m = $this->_m; assert (!!$m); $txt = trim ($txt) . ','; $res = array (); $l = mblen ($txt); $start = 0; $isQuoted = false; $tblqchar = $m->quotingChar; $ql = mblen ($tblqchar); for ($i = 0; $i < $l; ++$i) { switch (mb_substr ($txt, $i, 1)) { case ",": if ($isQuoted) continue; $s = trim (mb_substr ($txt, $start, $i - $start)); $start = $i + 1; // Remove quotes. if (mb_substr ($s, 0, $ql) == $tblqchar) $s = mb_substr ($s, $ql, mblen ($s) - $ql * 2); // Validate. if (!$s || mb_strpos ($s, $tblqchar) !== false) continue; // Store. $res [$s] = 1; break; case $tblqchar: $isQuoted = !$isQuoted; } } return $res; } /// METHODS FOR CONTROLLING THE LOADING PROCESS /// // Loads the SQL in F into DB. protected function _loadFromFile ($f, $onEachSqlStmt) { assert (!!$f); $sql = ''; while (!feof ($f)) { $ln = rtrim (fgets ($f)); $this->_buildSQLStmt ($sql, $ln, $onEachSqlStmt); } return $sql ? call_user_func_array (array ($this, $onEachSqlStmt), $sql) : NULL; } // Loads the SQL in TXT INTO DB. protected function _loadFromText ($txt, $onEachSqlStmt = '_execSQL') { $sql = ''; $lines = preg_split ('/(?:\r\n)|\n|\r/m', $txt); foreach ($lines as $ln) $this->_buildSQLStmt ($sql, $ln, $onEachSqlStmt); return $sql ? call_user_func_array (array ($this, $onEachSqlStmt), $sql) : NULL; } // Parses LN and creates an SQL statement, that when completed is issued against BD. protected function _buildSQLStmt (&$sql, $ln, $onEachSqlStmt) { // Ignore comments. if (!($ln = rtrim ($ln))) { $sql .= DbModel::KSQLNL; return; } if (mb_substr ($ln, 0, 1) == '#' || substr ($ln, 0, 2) == '--') return; if (!preg_match ('/^\s+/', mb_substr ($ln, 0, 1))) { // Starting new SQL statement. $sql = preg_replace ('/(?:\r\n)|\n|\r/m', DbModel::KSQLNL, $sql); $sql && call_user_func_array (array ($this, $onEachSqlStmt), $sql); $sql = $ln . DbModel::KSQLNL; } else $sql .= mb_substr ($ln, 1) . DbModel::KSQLNL; // Resuming the last SQL statement. } // Simply execute SQL. protected function _execLoadingStmt ($sql) { $this->_m->execSQL ($sql); } // Run SQL and print results. protected function _queryAndPrint ($sql) { $m = $this->_m; $vw = $this->_vw; $vw->startPrintingQuery ($sql); $qres = $m->query ($sql); // Process the results (there may be more then one). while ($res = $qres->nextResult ()) { // Test if this is an error. if (is_object ($res)) { $vw->ackQueryError ($res->getCode (), $res->getMessage (), $sql); continue; } // Extract field names. $fldnms = array (); if ($res && !is_bool ($res)) { $row =& $res [0]; foreach ($row as $fldnm => $x) { if (is_int ($fldnm)) continue; $fldnms [] = $fldnm; } } $vw->printRecords ($fldnms, $res, $sql); $vw->flush (); } // Print warnings. if ($this->_showWarnings && ($warns = $qres->warnings)) { foreach ($warns as $w) $vw->ackQueryWarning ($w->code, $w->message, $sql); } $vw->endPrintingQuery (); } } /// DbModel /// /// The "juice" of the script. /// class DbModel { const KDumpTableStructureAndData = 0; const KDumpTableStructureOnly = 1; const KDumpDataOnly = 2; const KDumpNonDataOnly = 3; const KSQLNL = "\r\n"; const KMaxPacketData = 4096; public $observer; public $haltOnError; public $errorCount = 0; public $quotingChar = '`'; public $dumpingTablesFilter; // List of the table names to dump (if it's empty all are dumped). protected $_conn; protected $_connInfo; protected $_deftCharset = ''; protected $_deftCollation = ''; protected $_selfDestruct = false; protected $_pendingStmts = array (); public function __construct ($connInfo, $observer, $deftCharset = NULL) { $this->_conn = $this->_connect ($connInfo); $this->_connInfo = $connInfo; $this->observer = $observer ? $observer : new NullDbModelObserver; } public function __destruct () { $this->_conn && mysqli_close ($this->_conn); } // Select the character-set to use. public function setCharset ($deftCharset, $deftCollation = '') { $this->_deftCharset = $deftCharset; $this->_execSQL ("SET NAMES $deftCharset"); $this->_deftCollation = $deftCollation; } // Runs one or more SQL statement and returns the resulting rows. public function query ($sql) { $conn = $this->_conn; mysqli_multi_query ($conn, $sql); return new QueryResults ($conn, $this); } // Runs one SQL statement and returns true if there was no error. public function execSQL ($sql) { return !!$this->_execSQL ($sql); } // Dumps the database contents. public function dump ($whatToDump, $dropFirst, $onlyNewTables) { $o = $this->observer; // Before dumping, run the Stored Proc "__clean". This allows making some cleanup to the Database before // dumping. No harm done if the procedure doesn't exist. $this->_execSQL ('CALL __clean()', true, array (1305 => NULL)); // First, analyse tables. $rs = $this->_query ('SHOW FULL TABLES'); foreach ($rs as $row) { $tblnm = $row [0]; $this->_analyseTable ($tblnm, $tblsInfo); } // First of all set the charset. if ($deftCharset = $this->_deftCharset) { $o->msg ("-- CONFIG --\n"); $o->msg ("SET NAMES $deftCharset;"); $o->msg ("SET FOREIGN_KEY_CHECKS = 0;\n"); } if ($whatToDump !== self::KDumpNonDataOnly) { // Drop relations, so we can re-create tables if we need to. $o->msg ("-- DROP RELATIONS (will be rebuilt later) --\n"); if ($sqllst = $this->_getSQLForRemovingConstraints ($tblsInfo, true)) foreach ($sqllst as $sql) $o->msg ($sql); // Print table definitions. $a =& $this->dumpingTablesFilter; $o->msg ("\n\n-- TABLES --\n"); $o->notify ('Tables'); foreach ($tblsInfo as $ti) { if ($a && !array_key_exists ($ti->name, $a)) continue; $ti->isView || $this->_printTable ($ti, $whatToDump, $dropFirst, $onlyNewTables); } } // Print views definition. $o->msg ("\n\n-- VIEWS --\n"); $o->notify ('Views'); foreach ($tblsInfo as $ti) $ti->isView && $this->_printView ($ti); // Drop temp procedures. $o->msg ( "DROP PROCEDURE IF EXISTS __table_exists; DROP PROCEDURE IF EXISTS __table_exists_; DROP PROCEDURE IF EXISTS __create_table;"); // Create constraints. $o->msg ("\n\n-- CONSTRAINTS --\n"); $o->notify ("CONSTRAINTS"); $this->_printConstraints ($tblsInfo); // Create triggers. $o->msg ("\n\n-- TRIGGERS --\n"); $o->notify ("\nTRIGGERS"); $this->_printTriggers (); // Print "stored procs" definitions. $o->msg ("\n\n-- STORED PROCS --\n"); $o->notify ("STORED ROUTINES"); $this->_printStoredRoutines (); // Print pending statements. if ($pendingStmts =& $this->_pendingStmts) { $o->msg ("\n\n-- OTHER STATEMENTS --\n"); foreach ($pendingStmts as $sql) $o->msg ("$sql\n"); } return true; } // Start a loading process. Use 'query' or 'execSQL' for running the SQL. public function startLoading () {} // Abort the loading process. public function abortLoading () { $this->_pendingStmts = NULL; } // End the loading process. public function endLoading () { // Flush the statements that depend on objects that should be created by now. if (!($a = $this->_pendingStmts)) return; $this->_pendingStmts = NULL; foreach ($a as $o) $o->i = 0; // Let's try the statements a number times cause they can depend on each other. $n = sizeof ($a) + 2; foreach ($a as $o) { if ($o->i === NULL) continue; $sql = $o->sql; if ($o->i++ > $n) { // Stop trying - print the error. $this->_queryErrorMsg ($sql); $o->i = NULL; continue; } // Try running it. $this->_execSQL ($sql); $this->observer->ackPendingStmt ($sql); } } // Drops all views, stored procedures and functions and triggers (everyting but constraints). public function clearNonData () { // Drop triggers. $rs = $this->_query ('SHOW TRIGGERS'); foreach ($rs as $row) { $nm = $row ['Trigger']; $this->_execSQL ("DROP TRIGGER `$nm`;"); } // Drop Views. $rs = $this->_query ('SHOW FULL TABLES'); foreach ($rs as $row) { $nm = $row [0]; if ($row ['Table_type'] === 'VIEW') $this->_execSQL ("DROP VIEW `$nm`;"); } // Drop Stored Procs and Functions. $rs = $this->_query ('SHOW PROCEDURE STATUS WHERE Db = DATABASE()'); foreach ($rs as $row) { $nm = $row ['Name']; $this->_execSQL ("DROP PROCEDURE `$nm`;"); } $rs = $this->_query ('SHOW FUNCTION STATUS WHERE Db = DATABASE()'); foreach ($rs as $row) { $nm = $row ['Name']; $this->_execSQL ("DROP FUNCTION `$nm`;"); } } // Clear all constraints (primary keys, foreign keys, etc). public function clearConstraints () { $tblsInfo = NULL; // Get table info. $rs = $this->_query ('SHOW FULL TABLES'); foreach ($rs as $row) { $nm = $row [0]; // Ignore views. if ($row ['Table_type'] === 'VIEW') continue; $this->_analyseTable ($nm, $tblsInfo); } if (!$tblsInfo) return; // Drop constraints. /// First, remove foreign-key constraints. if ($sqllst = $this->_getSQLForRemovingConstraints ($tblsInfo, true)) foreach ($sqllst as $sql) $this->_execSQL ($sql); /// Then, remove all others. if ($sqllst = $this->_getSQLForRemovingConstraints ($tblsInfo, false)) foreach ($sqllst as $sql) $this->_execSQL ($sql); } // Return the names of the tables. IF LIST-VIEWS-ALSO is true, view names are also returned. public function getTableNames ($listViewsAlso = false) { $res = array (); $sql = $listViewsAlso ? 'SHOW TABLES' : 'SHOW FULL TABLES'; $rs = $this->_query ($sql); if ($listViewsAlso) { foreach ($rs as $row) $res [] = $row [0]; } else { foreach ($rs as $row) { if (strtolower ($row [1]) === 'view') continue; $res [] = $row [0]; } } return $res; } /// METHODS FOR EXPORTING /// // Stores info about the table NM in TBL-INFO. protected function _analyseTable ($nm, &$tblsInfo) { $conn = $this->_conn; $nm = mysqli_escape_string ($conn, $nm); // Is it a "view"?. $rs = $this->_query ('SHOW CREATE TABLE `' . $nm . '`'); $rs =& $rs [0]; if (!$rs) return; $fldnm = 'Create Table'; if (!array_key_exists ($fldnm, $rs)) { $isView = true; $fldnm = 'Create View'; } else $isView = false; $lines = preg_split ('/(?:(?:\r\n)|\r|\n)+/s', $rs [$fldnm]); // Store (and remove) "constraints" and format. $sql = $autoFieldDef = ''; $c = array (); foreach ($lines as $ln) { if (preg_match ('/^\s*CONSTRAINT|(?:PRIMARY\s+)?KEY|UNIQUE\s+KEY\s+()/', $ln)) { // If it does not end with a coma, remove it from the previous line. if (mb_substr ($ln, -1) !== ',') $sql = mb_substr ($sql, 0, -1); else $ln = mb_substr ($ln, 0, -1); $c [] = preg_replace ('/\s+/m', ' ', trim ($ln)); } else $sql .= "\n\t" . trim ($ln); } $sql = trim ($sql); // Note if theres an auto_increment field (and remove it). if (preg_match ('/[\(,]\s*(`([^`]+)`.*?\s+auto_increment)(?:\s+|,|\))/m', $sql, $m)) { $autoFieldDef = '`' . $m [2] . '` ' . $m [1]; } // Store the info collected. $o = new stdclass; $o->name = $nm; $o->isView = $isView; $o->autoFieldDef = $autoFieldDef; $o->fields =& $c; $o->sql =& $sql; $tblsInfo [$nm] = $o; } // Print the definition of the table in TBL-INFO. protected function _printTable ($tblInfo, $whatToDump, $dropFirst, $onlyNewTables) { $o = $this->observer; $nm = $tblInfo->name; if ($whatToDump !== self::KDumpDataOnly) { $sql = $tblInfo->sql; // Autonumbers are not allowed yet. They will be set later. if ($tblInfo->autoFieldDef) $sql = preg_replace('/\s+auto_increment((?:\s+|,|\)))/', '$1', $sql); // If omitted put some default charset info. if (($deftCharset = $this->_deftCharset)&& !preg_match ('/\).*(?:s+DEFAULT)?\s+CHARSET=.*$/i', $sql)) $sql .= ' DEFAULT CHARSET=' . $deftCharset; if (($deftCollation = $this->_deftCollation)&& !preg_match ('/\).*(?:\s+DEFAULT)?\s+COLLATE=.*$/i', $sql)) $sql .= ' DEFAULT COLLATE=' . $deftCollation; $sql .= ";"; } else $sql = ''; $o->msg ("---- Table \"$nm\" --\n"); $o->notify ($nm, 2); // In order to support some advanced options, tables are created through a temp Stored Proc. $drop = ''; // Recreate tables? if ($dropFirst && ($whatToDump !== self::KDumpDataOnly)) { $drop = "DROP TABLE IF EXISTS `$nm`;\n"; } // Insert data? $insertDataSQL = $whatToDump !== self::KDumpTableStructureOnly ? $this->_getTableDataSQL ($nm, $tblInfo) : ''; // Only create new tables? if ($onlyNewTables) { // Create a procedure that will catch errors presumably related to the non-existance of the table. The // procedure it calls does all the work. $o->msg ( "DROP PROCEDURE IF EXISTS `__table_exists`; DROP PROCEDURE IF EXISTS `__table_exists_`; CREATE PROCEDURE `__table_exists_`() BEGIN SELECT * FROM `$nm` LIMIT 0; END CREATE PROCEDURE `__table_exists`(OUT _res INT) BEGIN DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET _res = 0; SET _res = 1; CALL __table_exists_(); END\n"); // Create the main procedure. $sql = $this->_indent ($sql, 2); $insertDataSQL = $this->_indent ($insertDataSQL, 2); $o->msg ( "DROP PROCEDURE IF EXISTS `__create_table`; CREATE PROCEDURE `__create_table`() BEGIN DECLARE e INT; CALL __table_exists(e); IF e = 0 THEN $sql $insertDataSQL END IF; END"); $o->msg ("CALL __create_table('$nm');\n"); } else { $insertDataSQL && ($insertDataSQL = "\n\n$insertDataSQL\n"); $o->msg ($drop . $sql . $insertDataSQL); } } // Print the definition of the view in TBL-INFO. protected function _printView ($tblInfo) { $o = $this->observer; $nm = $tblInfo->name; $sql = $tblInfo->sql; $dbName = $this->_connInfo->dbName; // Remove the definer info. if (($i = mb_stripos ($sql, 'VIEW')) !== false) $sql = mb_substr ($sql, $i); // Remove explicit references to the database. $sql = preg_replace ("/(?:`$dbName`|\b$dbName)\./i", ' ', trim ($sql)); // Replace if already exists. $sql = 'CREATE OR REPLACE ' . $sql . ";\n"; $o->msg ("---- View \"$nm\" --\n"); $o->notify ($nm, 2); $o->msg ($sql); } // Returns SQL for inserting data in table NM. protected function _getTableDataSQL ($nm, $tblInfo) { $res = NULL; $conn = $this->_conn; $o = $this->observer; $pendingStmts =& $this->_pendingStmts; $nm = mysqli_escape_string ($conn, $nm); $h = $this->_execSQL ('SELECT * FROM `' . $nm . '`'); if (!$h) return; $rs = array (); while ($row = mysqli_fetch_array ($h, MYSQLI_BOTH)) $rs [] = $row; $maxIncrVal = -1; // Get field data. $columns = mysqli_fetch_fields ($h); mysqli_free_result ($h); if (!$rs) return; $res = ''; $flds = ''; $n = sizeof ($columns); for ($i = 0; $i < $n; ++$i) $flds .= ',`' . mysqli_escape_string ($conn, $columns [$i]->name) . '`'; $flds = mb_substr ($flds, 1); $insertStmt = "INSERT INTO `$nm` ($flds) VALUES"; // Éxtract the name of the auto-increment field. $autoIncrFldNm = ''; if ($tblInfo->autoFieldDef && preg_match('/`?([^`]+)/', $tblInfo->autoFieldDef, $m)) $autoIncrFldNm = $m [1]; // Print records. $sql = $insertStmt; $c = 0; foreach ($rs as $row) { $s = ''; ++$c; // Take care for not pushing the MySQL "KMaxPacketData" limit. if (mblen ($sql) > self::KMaxPacketData) { $res .= mb_substr ($sql, 0, -1) . ";\n"; $sql = $insertStmt; } // Get the values. foreach ($columns as $fld) { $val = $row [$fld->name]; $s .= ',' . $this->_getForSQL ($fld, $val); } // If theres an auto-incremented field... if ($autoIncrFldNm) { // Store the max auto-incremented value. $id = intval ($row [$autoIncrFldNm]); $id > $maxIncrVal && ($maxIncrVal = $id); // If the Id value is 0, Mysql will silently change it when we mark the field as "auto_increment" in // the end of the load process. We can solve this by simply inserting the record in the end. if ($id === 0) { $pendingStmts [] = "$insertStmt\n\t(" . mb_substr ($s, 1) . ');'; $pendingStmts [] = "UPDATE `$nm` SET `$autoIncrFldNm` = 0 WHERE `$autoIncrFldNm` = LAST_INSERT_ID();"; continue; } } $sql .= "\n\t(" . mb_substr ($s, 1) . '),'; } $res .= mb_substr ($sql, 0, -1); // Disallow negative auto-incremented values. if ($autoIncrFldNm && $maxIncrVal < 0 && $c > 0) $res .= "\nALTER TABLE `$nm` AUTO_INCREMENT = 1"; return $res . ";"; } // Return a list of SQL statements for removing all constraints from all tables. Id PRINT-FG-KEYS is false no // foreign-key constraints are printed. Otherwise, __only__ the foreign-key constraints are printed. protected function _getSQLForRemovingConstraints (&$tblInfo, $printFgKeys = false) { assert (!!$tblInfo); $sql = ''; // Drop all constraints (so they will be recreated next). foreach ($tblInfo as $tblnm => $o) { foreach ($o->fields as $s) { if (preg_match('/PRIMARY\s+KEY/m', $s)) { // Primary key. if ($printFgKeys) continue; // An auto_increment field must be PRIMARY. So if we want to drop the primary key, we have // to clear that attribute. if ($o->autoFieldDef) { $sql .= "ALTER TABLE `$tblnm` CHANGE " . trim (preg_replace ('/\s+auto_increment\s*/m', ' ', $o->autoFieldDef)) . ";\n\t"; } $s = 'PRIMARY KEY'; } else if (preg_match('/CONSTRAINT(?:\s*`([^`]+)`\s*|\s+(\S+)\s+)FOREIGN\s+KEY/m', $s, $m)) { // Relation. if (!$printFgKeys) continue; $keynm = $m [1]; $keynm || ($keynm = $m [2]); $s = 'FOREIGN KEY `' . $keynm . '`'; } else { // Index. if ($printFgKeys || !preg_match ('/KEY\s+(?:`([^`]+)`|(\S+))/m', $s, $m)) continue; $keynm = $m [1]; $keynm || ($keynm = $m [2]); $s = 'KEY `' . $keynm . '`'; } $sql .= "ALTER TABLE `$tblnm` DROP $s;\n\t"; } } $sql = rtrim ($sql); if (!$sql) return ''; $res = array (); // Create a procedure that does the dropping and ignores all the errors that could occur. $sql = "CREATE PROCEDURE `__clear_constraints`() BEGIN DECLARE x INT; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET x = 0; $sql END"; $res [] = 'DROP PROCEDURE IF EXISTS __clear_constraints;'; $res [] = $sql; $res [] = 'CALL __clear_constraints();'; $res [] = 'DROP PROCEDURE IF EXISTS __clear_constraints;'; return $res; } // Prints CONSTRAINTS. protected function _printConstraints (&$tblsInfo) { assert (!!$tblsInfo); $o = $this->observer; $sql = ''; if ($sqllst = $this->_getSQLForRemovingConstraints ($tblsInfo)) { foreach ($sqllst as $s) $sql .= "$s\n"; $sql = "$sql\n\n"; } // First create indexes... foreach ($tblsInfo as $tblnm => $tbli) { $o->notify ("Indexes for \"$tblnm\"", 2); $sql .= "---- Indexes for $tblnm --\n"; foreach ($tbli->fields as $s) { if (preg_match ('/CONSTRAINT\s+/m', $s)) continue; $sql .= "ALTER TABLE `$tblnm` ADD $s;\n"; } // If there was an auto_increment field, now it's time to restore it. if ($tbli->autoFieldDef) { $sql .= "ALTER TABLE `$tblnm` CHANGE " . $tbli->autoFieldDef . ";\n"; } } // Than, create all the other constraints. foreach ($tblsInfo as $tblnm => $tbli) { $o->notify ("Constraints for \"$tblnm\"", 2); $sql .= "---- Constraints for $tblnm --\n"; foreach ($tbli->fields as $s) { if (!preg_match ('/CONSTRAINT\s+/m', $s)) continue; $sql .= "ALTER TABLE `$tblnm` ADD $s;\n"; } } $o->msg ($sql); } // Imprime a definição dos "triggers" em DB. protected function _printTriggers () { $o = $this->observer; $rs = $this->_query ('SHOW TRIGGERS'); $sql = ''; foreach ($rs as $row) { $nm = trim ($row ['Trigger']); $o->notify ($nm, 2); $sql .= 'DROP TRIGGER `' . $nm . "`\n"; $sql .= 'CREATE TRIGGER ' . $nm . ' ' . $row ['Timing'] . ' ' . $row ['Event'] . ' ON ' . $row ['Table'] . "\n\tFOR EACH ROW " . ltrim ($this->_indent ($row ['Statement'])) . "\n\n"; } $o->msg (rtrim ($sql)); } // Prints the definition of the procedures in CONN. protected function _printStoredRoutines () { $o = $this->observer; $rsProcs = $this->_query ('SHOW PROCEDURE STATUS WHERE Db="' . $this->_connInfo->dbName . '"'); $rs = NULL; // Procedimentos. foreach ($rsProcs as $row) { $s = $row ['Name']; $o->notify ($s, 2); $o->msg ('---- PROCEDURE "' . $s . "\" --\n"); // Get the definition. $rs = $this->_query ('SHOW CREATE ' . $row ['Type'] . ' ' . $s); $rs =& $rs [0]; $procdef = $rs ['Create Procedure']; /// Remove "definer". $procdef = $this->_indent (preg_replace ('/\s+DEFINER=`[^`]+`@`[^`]+`\s*/i', ' ', $procdef)); $o->msg ("DROP PROCEDURE IF EXISTS `$s`"); $o->msg ($procdef . "\n"); } // Funções. $rsFuncs = $this->_query ('SHOW FUNCTION STATUS WHERE Db="' . $this->_connInfo->dbName . '"'); foreach ($rsFuncs as $row) { $s = $row ['Name']; $o->notify ($s, 2); $o->msg ('---- FUNCTION "' . $s . "\" --\n"); // Get the definition. $rs = $this->_query ('SHOW CREATE ' . $row ['Type'] . ' ' . $s); $rs =& $rs [0]; $funcdef = $rs ['Create Function']; /// Remove "definer". $funcdef = $this->_indent (preg_replace ('/\s+DEFINER=`[^`]+`@`[^`]+`\s*/i', ' ', $funcdef)); $o->msg ("DROP FUNCTION IF EXISTS `$s`"); $o->msg ($funcdef . "\n"); } } // Returns VAL formatted in the way a field of the type of FLD must be. protected function _getForSQL ($fld, $val) { $conn = $this->_conn; switch ($fld->type) { case MYSQLI_TYPE_TIMESTAMP: case MYSQLI_TYPE_DATETIME: if ($val === NULL) return 'NULL'; // Analisar se é a data 0. $l = strlen ($val); for ($i = 0; $i < $l; ++$i) { $ch = substr ($val, $i, 1); if ($ch > '0' && $ch <= '9') break; } if ($i >= $l) return '\'0000-00-00 00:00:00\''; // Converter. is_string ($val) && ($val = strtotime ($val)); return '\'' . date ('Y-m-d h:i:s', $val) . '\''; case MYSQLI_TYPE_TIME: if ($val === NULL) return 'NULL'; is_string ($val) && ($val = strtotime ($val)); return '\'' . date ('h:i:s', $val) . '\''; case MYSQLI_TYPE_DATE: case MYSQLI_TYPE_NEWDATE: if ($val === NULL) return 'NULL'; is_string ($val) && ($val = strtotime ($val)); return '\'' . date ('Y-m-d', $val) . '\''; case MYSQLI_TYPE_TINY_BLOB: case MYSQLI_TYPE_MEDIUM_BLOB: case MYSQLI_TYPE_LONG_BLOB: case MYSQLI_TYPE_BLOB: case MYSQLI_TYPE_VAR_STRING: case MYSQLI_TYPE_STRING: if ($val === NULL) return 'NULL'; return '\'' . mysqli_real_escape_string ($conn, $val) . '\''; case MYSQLI_TYPE_FLOAT: case MYSQLI_TYPE_DOUBLE: case MYSQLI_TYPE_DECIMAL: if ($val === NULL) return 'NULL'; if (is_string ($val) && $val != NULL) { $val = str_replace (",", ".", $val); } else $val = strval ($val); return $val; case 16: // BIT FIELD. if ($val === NULL) return 'NULL'; return ord ($val); default: if ($val === NULL) return 'NULL'; if (!is_string ($val)) $val = $val == '0' ? 0 : strval ($val); return $val; } } // Retorna S com um "tab" antes de cada linha. // Returns SQL with a TAB-COUNT tabs after each line break. protected function _indent ($sql, $tabCount = 1) { if (!$sql) return ''; $res = ''; $eol = "\n" . str_repeat ("\t", $tabCount); $lines = preg_split ('/(?:\r\n)|\r|\n/s', $sql); foreach ($lines as $ln) $res .= $eol . rtrim ($ln); return mb_substr ($res, mblen ($eol)); } // Tries to connect to the database. It returns false if it failed or if the connection info is not present. protected function _connect ($connInfo) { // Validate and connect. if (!$connInfo || !$connInfo->dbName || !$connInfo->user) throw new DbConnException ('Connection info missing (at least "Database Name" and "User Name" must be provided)'); if (!($conn = @mysqli_connect ($connInfo->server, $connInfo->user, $connInfo->pwd, $connInfo->dbName, $connInfo->portNum))) throw new DbConnException ('ERROR #' . mysqli_connect_errno () . ': ' . mysqli_connect_error ()); return $conn; } // Runs SQL and returns the result in an array. protected function _query ($sql) { if (!($h = $this->_execSQL ($sql))) return NULL; if (is_bool ($h)) return $h; $res = array (); while ($row = mysqli_fetch_array ($h, MYSQLI_BOTH)) $res [] = $row; mysqli_free_result ($h); return $res; } // Executes SQL against the DB. It doesn't expect the query to return records. protected function _execSQL ($sql, $reopenIfDown = true, $ignoredErrList = NULL) { $sql = trim ($sql); if (!$sql) return NULL; $o = $this->observer; if ($res = mysqli_query ($conn = $this->_conn, $sql)) { $o->ackSuccStmt ($sql); return $res; } // Reconnect if the connection is lost. $errn = mysqli_errno ($conn); if ($reopenIfDown && ($errn == 2013 || $errn == 2006)) { $this->_connect ($this->_connInfo); return $this->_execSQL ($sql, false, $ignoredErrList); } // Ignore certain errors. if ($ignoredErrList && array_key_exists ($errn, $ignoredErrList)) return NULL; $o->ackFailedStmt ($errn, mysqli_error ($conn), $sql); $this->_queryErrorMsg ($sql); return NULL; } // Prints the error in CONN. protected function _queryErrorMsg ($sql) { if (strtoupper (mb_substr ($sql, 0, 13)) == 'DROP TRIGGER ') return NULL; $pendingStmts =& $this->_pendingStmts; $conn = $this->_conn; $err = mysqli_errno ($conn); $errMsg = mysqli_error ($conn); // A view can depend on another that isn't created yet. Let's postpone those statements. if (($err === 1146) && preg_match ('/CREATE.*?VIEW/m', $sql)) { $o = new stdclass; $o->sql = $sql; $pendingStmts [] = $o; return NULL; } ++$this->errorCount; $this->observer->queryError ($err, $errMsg, $sql); if ($this->haltOnError) throw new DbModelHalt; } } /// QueryResults /// /// Handles the results of a query. /// class QueryResults { public $count; public $warnings; protected $_res; protected $_ndx = 0; public function __construct ($conn, $m) { $res = array (); $warnings = array (); // Process results now. do { $qres = mysqli_store_result ($conn); if (!$qres) { if ($errn = mysqli_errno ($conn)) { // Take note of the error. $res [] = new DbQueryException (mysqli_error ($conn), $errn); } else $res [] = NULL; // No records returned. } else { // Store the records returned. $recs = array (); while ($row = mysqli_fetch_array ($qres, MYSQLI_BOTH)) $recs [] = $row; $res [] = $recs; } @mysqli_free_result ($qres); } while (mysqli_next_result ($conn)); // Take note of errors and warnings. if ($h = mysqli_query ($conn, 'SHOW WARNINGS')) { while ($row = mysqli_fetch_array ($h, MYSQLI_BOTH)) { $code = $row ['Code']; $msg = $row ['Message']; if ($row ['Level'] === 'Error') { // Make sure we're not reporting this error twice. foreach ($res as $e) { if (!is_object ($e)) continue; if ($e->getCode () == $code && $e->getMessage () === $msg) continue 2; } // Store the error. $res [] = new DbQueryException ($msg, $code); } else { // Just a warning. $o = new stdclass; $o->code = $code; $o->message = $msg; $warnings [] = $o; } } mysqli_free_result ($h); } $this->_res =& $res; $this->count = sizeof ($res); $this->warnings =& $warnings; } // Gets the next resultset. public function nextResult () { if ($this->_ndx >= $this->count) return false; return $this->_res [$this->_ndx++]; } } /// AHTMLView /// /// This is the base "view" of the script /// abstract class AHTMLView { public $title = 'i-senso load/dump'; public $processFinishedMsg = ''; protected $_controller; protected $_connInfo; protected $_errorMsgs = array (); protected $_processing = false; protected $_bodyClass = 'wnd'; protected $_outDivCSSClass = 'out'; const kMaxColumnWidth = 35; // Max column width in "ems". const kDateTimeFormat = 'Y-m-d H:i:s'; const kDateFormat = 'Y-m-d'; const kTimeFormat = 'H:i:s'; const kMaxDisplayableRows = 256; public function __construct ($controller) { $this->_controller = $controller; } // Print page layout. Somewhere in the middle there's a placeholder for inserting actual content. protected function _printPageTemplate () { ?> _inHTMLHeader (); $tit = $this->title; if (mblen ($tit) > 35) $tit = mb_substr ($tit, 0, 35); $tit = htmlspecialchars ($tit); ?> i-senso transSQL<?php if ($tit) echo " - $tit"; ?> . $tit = preg_replace ('/([^"]+)"([^"]+)"(.*)/ms', '$1$2$3', str_replace ('"', '"', $tit)); if (DEBUGGING) { ?>
i-senso/2006-2007

Filipe Manuel da Silva Martins

http://www.i-senso.com
transSQL/MySQL ver

[ ]

_printContents (); if (!$this->_controller->isConnectedToDb) { ?> _errorMsgs) foreach ($errorMsgs as $errmsg) { ?>

_encodeMsg ($errmsg); ?>

_printPageContent (); $this->flush (); // Start processing. Messages and notifications will be presented to the user. $this->_processing = true; // From now on, errors and notifications will be printed immediatly. ?>
_process (); if ($msg = $this->processFinishedMsg) { ?>

_encodeMsg ($msg); ?>

_connInfo; if (!$this->_controller->isConnectedToDb) { ?> Connection info Server and Port User Name Password Database Name
_processing) { // Just store the message for now. $this->_errorMsgs [] = $msg; } else { ?>

_encodeMsg ($msg); ?>

flush (); } } // Be informed that a query error was encountered and should be displayed. public function ackQueryError ($errnum, $msg, $sql) { ?>

ERROR #: _encodeMsg ($msg); ?>

_encodeMsg ($sql); ?>
flush (); } // Be informed that a query error was encountered and should be displayed. public function ackQueryWarning ($errnum, $msg, $sql) { ?>

WARNING #: _encodeMsg ($msg); ?>

_encodeMsg ($sql); ?>
flush (); } // A message was issued and should be displayed. public function ackMsg ($msg) { assert ($this->_processing); ?>

_encodeMsg ($msg); ?>

flush (); } // Be informed that a notification was issued and should be displayed. public function ackNotification ($msg, $level = 1) { assert ($this->_processing); ?>

_encodeMsg ($msg); ?>

flush (); } // Encodes MSG for being printed safely on a web page. protected function _encodeMsg ($msg) { $msg = htmlspecialchars ($msg); $msg = str_replace ("\n", '
', $msg); $msg = str_replace ("\t", '    ', $msg); return $msg; } // Return true if S is a valid date. T flags what type of date it is: d == date; te == time; dt == datetime. protected function _isDate ($s, &$t) { $t = NULL; if (!function_exists ('date_parse')) return false; $res = date_parse ($s); if (!$res || @$res ['error_count']) return false; // Analyse 'res'. $n = 0; foreach ($res as $fldnm => $v) { if (!$v) continue; switch ($fldnm) { case 'hour': case 'minute': case 'second': case 'fraction': $t = $t == 'd' ? 'dt' : 'te'; break; case 'year': case 'month': case 'day': $t = $t == 'te' ? 'dt' : 'd'; break; } if ($t == 'dt') return true; } return !!$t; } // Return V formatted according it's type (T). protected function _formatVal ($v, $t) { if ($v === NULL) return 'NULL'; switch ($t) { case 'r': // Real. return number_format (floatval($v), 2); case 'i': // Integer. return number_format (intval ($v), 0); case 'dt': // Date-time. return date (self::kDateTimeFormat, strtotime ($v)); case 'd': // Date. return date (self::kDateFormat, strtotime ($v)); case 'te': // Time. return date (self::kTimeFormat, strtotime ($v)); case 'b': return intval ($v) ? 'true' : 'false'; default: return addslashes ($v); } } // Print the page contents. abstract protected function _printPageContent (); // Do the page processing. abstract protected function _process (); } /// HTMLView_Welcome /// /// This is the "view" part of the script /// class HTMLView_Welcome extends AHTMLView { // Render the main page. public function render () { $this->_printPageTemplate (); } // Just invite the controller to query. protected function _process () {} // Print the main page. protected function _printPageContent () { ?>

Welcome to transSQL/MySQL

This script lets you dump a whole local database and load it to a remote host (or vice-versa).

The differences between this script and the others are:

transSQL dumps the whole database: tables, views, stored procs, triggers, constraints... the works.

The script knows that a view depends on tables or other views. It is also aware that two tables can be linked to one another via a foreign key.

When loading, unless instructed otherwise, the script will continue working even if errors occur. In that case, it prints the error message and the statement that caused it (so you can correct it later). In fact, successful statements are logged to a file and failed ones are logged to another, so everything gets registered.

transSQL is a single file. Just copy it to your server's webroot and everything (should) work, as long as you have a proper PHP and MySQL installation.


So, what do you want to do now?

Dump Load Query at will

Surely won't be needed but... report a bug.

. protected function _inHTMLHeader () { ?> _connInfo = $connInfo; $this->_deftCharset = $deftCharset; $this->_deftCollation = $deftCollation; $this->_tables = $tables; $this->_whatToDump = $whatToDump; $this->_dropFirst = $dropFirst; $this->_onlyNewTables = $onlyNewTables; $this->_fn = $fn; $this->_saveFile = $saveFile; $this->_printPageTemplate (); } // Create the form for dumping. protected function _printPageContent () { $whatToDump = $this->_whatToDump; ?>
_printAuthFields (); ?>
Locale settings Charset and Collation

Dump Tables (click to select)
What to dump

Options
_dropFirst) echo ' checked="checked"'; ?> onclick="this.checked&&(elm('chkOnlyNewTables').checked=false)" />
_onlyNewTables) echo ' checked="checked"'; ?> onclick="this.checked&&(elm('chkNoDrop').checked=false)" />
_saveFile) echo ' checked="checked"'; ?> /> _saveFile) echo ' disabled="disabled"'; ?> />
_controller->vw_dump (); } } /// HTMLView_Load /// /// The "load" screen. /// class HTMLView_Load extends AHTMLView { protected $_fl; protected $_sql; protected $_selfDestruct; protected $_haltOnError; protected $_clearNonData; protected $_clearConstraints; // Render the load page. public function render ($connInfo, $fl, $sql, $selfDestruct, $haltOnError, $clearNonData, $clearConstraints) { $this->_connInfo = $connInfo; $this->_fl = $fl; $this->_sql = $sql; $this->_selfDestruct = $selfDestruct; $this->_haltOnError = $haltOnError; $this->_clearNonData = $clearNonData; $this->_clearConstraints = $clearConstraints; $this->_printPageTemplate (); } // Just invite the controller to load SQL. protected function _process () { $this->_controller->vw_load (); } // Print the form for loading. protected function _printPageContent () { ?>
_printAuthFields (); ?>
Options
_haltOnError) echo ' checked="checked"'; ?> />
_selfDestruct) echo ' checked="checked"'; ?> />
_clearNonData) echo ' checked="checked"'; ?> onclick="if(this.checked)alert('WARNING!! Make sure the SQL you\'re loading will rebuild ALL stored procs, functions, triggers, etc.')" />
_clearConstraints) echo ' checked="checked"'; ?> onclick="if(this.checked)alert('WARNING!! Make sure the SQL you\'re loading will rebuild ALL constraints.')" />

Source File
Upload file
Direct SQL Input
_connInfo = $connInfo; $this->_sql = $sql; $this->_charset = $charset; $this->_collation = $collation; $this->_haltOnError = $haltOnError; $this->_showWarnings = $showWarnings; $this->_printPageTemplate (); } // Start printing the results a query. public function startPrintingQuery ($sql) { ?>

_controller->vw_query (); } // Print the page contents. protected function _printPageContent () { ?>
_printAuthFields (); ?>
Locale Charset and Collation

Options
_haltOnError) echo ' checked="checked"'; ?> />
_showWarnings) echo ' checked="checked"'; ?> />

Query like there's no tomorrow

No rows returned from

No rows returned

_isDate ($v, $subtype)) { if ($t !== 'dt') $t = $subtype; } else $t = 't'; $coltypes [$i] = $t; } // If there's too much records then give up understanding them all. if (++$rowCount > 64) { for ($i = 0; $i < $fldcount; ++$i) $coltypes [$i] = 't'; break; } } // Adjust column width, make sure they aren't too wide for display, and calc total width. $tw = 0; for ($i = 0; $i < $fldcount; ++$i) { $w = $colwidths [$i]; if ($w > self::kMaxColumnWidth) $w = self::kMaxColumnWidth; // If displaying numbers we don't need as much space. $factor = 1; switch ($coltypes [$i]) { case 'i': $factor = 0.7; break; case 'r': $factor = 0.85; break; default: $factor = 0.95; } $w = floor ($w * $factor); $colwidths [$i] = $w; $tw += $w; } // Print record list header. ?>
self::kMaxDisplayableRows) { $rowCount = -1; break; } ?>>_formatVal ($rec [$i], $t); if ($v === '') $v = ' '; // IE won't print borders on an empty cell... else if ($v === NULL) $v = 'NULL'; else $v = htmlspecialchars ($v); ?> 5) { ?>
. protected function _inHTMLHeader () { ?>

ERROR #: _encodeMsg ($msg); ?>

flush (); } // Be informed that a query error was encountered and should be displayed. public function ackQueryWarning ($errnum, $msg, $sql) { ?>

WARNING #: _encodeMsg ($msg); ?>

flush (); } } /// HTMLView_ShowTableList /// /// The "Select Tables List" screen. /// class HTMLView_ShowTableList extends AHTMLView { protected $_bodyClass = 'pop'; protected $_tbllst; protected $_tblnmQuote; // Render the page for selecting tables (used on a popup window). public function render ($tbllst, $tblnmQuote) { $this->_tbllst =& $tbllst; $this->_tblnmQuote = $tblnmQuote; $this->_printPageTemplate (); } // Just invite the controller to query. protected function _process () {} // Print the page for selecting tables (used on a popup window). TBL-LST is the list of table names. // TBL-NM-QUOTE is the quoting char for surrounding table names. protected function _printPageContent () { $tbllst =& $this->_tbllst; if ($tbllst === NULL) return; ?>

Select the tables you wish to dump. If none is selected, all will be dumped. When you're done, just close the window.

. protected function _inHTMLHeader () { ?> _vw = $vw; } // Print an error. public function error ($msg) {} // Print a query error. public function queryError ($errnum, $msg, $sql) { $this->_vw->ackQueryError ($errnum, $msg, $sql); } // Print a message. public function msg ($msg, $level = 2) {} // Print a notification. public function notify ($msg, $level = 1) {} // Acknowledge a successul sql statement. public function ackSuccStmt ($sql) {} // Acknowledge a sql statement that was postponed until other objects are created. public function ackPendingStmt ($sql) {} // Acknowledge a failed sql statement. public function ackFailedStmt ($errn, $errmsg, $sql) {} } /// NullDbModelObserver /// /// A dumb observer that does nothing. /// class NullDbModelObserver extends ADbModelObserver {} /// DbModelLogger /// /// A dumb observer that does nothing. /// class DbModelLogger extends ADbModelObserver { protected $_succLogFN; protected $_failedLogFN; protected $_pendLogFN; public function __construct ($vw, $succLogFN, $pendLogFN, $failedLogFN) { parent::__construct ($vw); $this->_succLogFN = $succLogFN; $this->_failedLogFN = $failedLogFN; $this->_pendLogFN = $pendLogFN; } // Acknowledge a successul sql statement. public function ackSuccStmt ($sql) { $this->_dump ($this->_succLogFN, $sql); } // Acknowledge a sql statement that was postponed until other objects are created. public function ackPendingStmt ($sql) { $this->_dump ($this->_pendLogFN, $sql); } // Acknowledge a failed sql statement. public function ackFailedStmt ($errn, $errmsg, $sql) { $s = "ERROR # $errn: $errmsg!\nSQL: $sql\n" . str_repeat ('-', 60) . "\n\n"; file_put_contents ($this->_failedLogFN, $s, FILE_APPEND); } // Append a SQL statement to a log file. protected function _dump ($fn, $sql) { file_put_contents ($fn, $sql . "\n" . str_repeat ('-', 60) . "\n\n", FILE_APPEND); } } /// ObserverFile /// /// Sends its input to a file. /// class ObserverFile extends DbModelLogger { protected $_f; public function __construct ($vw, $succLogFN, $pendLogFN, $failedLogFN, $fn) { parent::__construct ($vw, $succLogFN, $pendLogFN, $failedLogFN); if (!($this->_f = @fopen ($fn, 'w'))) throw new FileException ("Couldn't open \"$fn\""); } public function __destruct () { fclose ($this->_f); } // Print an error. public function error ($msg) { fputs ($this->_f, "\n\nERROR: $msg\n\n"); } // Print a message. public function msg ($msg, $level = 2) { fputs ($this->_f, "$msg\n"); } // Print a notification. public function notify ($msg, $level = 1) { $this->_vw->ackNotification ($msg, $level); } } /// ObserverWeb /// /// Sends its input to the View. /// class ObserverWeb extends DbModelLogger { // Print an error. public function error ($msg) { $this->_vw->ackError ("$msg"); } // Print a message. public function msg ($msg) { $this->_vw->ackMsg ("$msg"); } } /// EXCEPTION CLASSES /// /// Just for error signaling. /// class DbModelHalt extends Exception {} class ValidationException extends Exception {} class DbConnException extends Exception {} class DbQueryException extends Exception {} class FileException extends Exception {} ?>