예제 #1
0
 /**
  * {@inheritdoc}
  *
  * @throws file_updater_failure_exception	When the file is not writable
  * @throws filesystem_exception				When the filesystem class fails
  */
 public function update_file($path_to_file_to_update, $source, $create_from_content = false)
 {
     $path_to_file_to_update = $this->phpbb_root_path . $path_to_file_to_update;
     $original_file_perms = false;
     // Maybe necessary for binary files
     $dir = dirname($path_to_file_to_update);
     if (!$this->filesystem->exists($dir)) {
         $this->make_dir($dir);
     }
     if (!$this->filesystem->is_writable($path_to_file_to_update)) {
         // Extract last 9 bits we actually need
         $original_file_perms = @fileperms($path_to_file_to_update) & 511;
         $this->filesystem->phpbb_chmod($path_to_file_to_update, filesystem::CHMOD_WRITE);
     }
     if (!$create_from_content) {
         try {
             $this->filesystem->copy($source, $path_to_file_to_update, true);
         } catch (filesystem_exception $e) {
             $this->write_file($path_to_file_to_update, $source, $create_from_content);
         }
     } else {
         $this->write_file($path_to_file_to_update, $source, $create_from_content);
     }
     if ($original_file_perms !== false) {
         $this->filesystem->phpbb_chmod($path_to_file_to_update, $original_file_perms);
     }
 }
예제 #2
0
 /**
  * proxy_instantiator constructor
  * @param string $cache_dir Cache dir for fall back when using open_basedir
  */
 public function __construct($cache_dir)
 {
     $config = new Configuration();
     // Prevent trying to write to system temp dir in case of open_basedir
     // restrictions being in effect
     $ini_wrapper = new IniGetWrapper();
     $filesystem = new filesystem();
     $tmp_dir = function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '';
     if (empty($tmp_dir) || $ini_wrapper->getString('open_basedir') && (!$filesystem->exists($tmp_dir) || !$filesystem->is_writable($tmp_dir))) {
         $config->setProxiesTargetDir($cache_dir);
     }
     $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
     $this->factory = new LazyLoadingValueHolderFactory($config);
 }
예제 #3
0
 /**
  * The function which does the actual work (or dispatches it to the relevant places)
  */
 function convert_data($converter)
 {
     global $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache, $auth;
     global $convert, $convert_row, $message_parser, $skip_rows, $language;
     global $request, $phpbb_dispatcher;
     $phpbb_config_php_file = new \phpbb\config_php_file($phpbb_root_path, $phpEx);
     extract($phpbb_config_php_file->get_all());
     require_once $phpbb_root_path . 'includes/constants.' . $phpEx;
     require_once $phpbb_root_path . 'includes/functions_convert.' . $phpEx;
     $dbms = $phpbb_config_php_file->convert_30_dbms_to_31($dbms);
     /** @var \phpbb\db\driver\driver_interface $db */
     $db = new $dbms();
     $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, true);
     unset($dbpasswd);
     // We need to fill the config to let internal functions correctly work
     $config = new \phpbb\config\db($db, new \phpbb\cache\driver\dummy(), CONFIG_TABLE);
     // Override a couple of config variables for the duration
     $config['max_quote_depth'] = 0;
     // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues
     $config['max_post_chars'] = $config['min_post_chars'] = 0;
     // Set up a user as well. We _should_ have enough of a database here at this point to do this
     // and it helps for any core code we call
     $user->session_begin();
     $user->page = $user->extract_current_page($phpbb_root_path);
     $convert->options = array();
     if (isset($config['convert_progress'])) {
         $convert->options = unserialize($config['convert_progress']);
         $convert->options = array_merge($convert->options, unserialize($config['convert_db_server']), unserialize($config['convert_db_user']), unserialize($config['convert_options']));
     }
     // This information should have already been checked once, but do it again for safety
     if (empty($convert->options) || empty($convert->options['tag']) || !isset($convert->options['dbms']) || !isset($convert->options['dbhost']) || !isset($convert->options['dbport']) || !isset($convert->options['dbuser']) || !isset($convert->options['dbpasswd']) || !isset($convert->options['dbname']) || !isset($convert->options['table_prefix'])) {
         $this->error($user->lang['NO_CONVERT_SPECIFIED'], __LINE__, __FILE__);
     }
     $this->template->assign_var('S_CONV_IN_PROGRESS', true);
     // Make some short variables accessible, for easier referencing
     $convert->convertor_tag = basename($convert->options['tag']);
     $convert->src_dbms = $convert->options['dbms'];
     $convert->src_dbhost = $convert->options['dbhost'];
     $convert->src_dbport = $convert->options['dbport'];
     $convert->src_dbuser = $convert->options['dbuser'];
     $convert->src_dbpasswd = $convert->options['dbpasswd'];
     $convert->src_dbname = $convert->options['dbname'];
     $convert->src_table_prefix = $convert->options['table_prefix'];
     // initiate database connection to old db if old and new db differ
     global $src_db, $same_db;
     $src_db = $same_db = null;
     if ($convert->src_dbms != $dbms || $convert->src_dbhost != $dbhost || $convert->src_dbport != $dbport || $convert->src_dbname != $dbname || $convert->src_dbuser != $dbuser) {
         $dbms = $convert->src_dbms;
         /** @var \phpbb\db\driver\driver $src_db */
         $src_db = new $dbms();
         $src_db->sql_connect($convert->src_dbhost, $convert->src_dbuser, htmlspecialchars_decode($convert->src_dbpasswd), $convert->src_dbname, $convert->src_dbport, false, true);
         $same_db = false;
     } else {
         $src_db = $db;
         $same_db = true;
     }
     $convert->mysql_convert = false;
     switch ($src_db->sql_layer) {
         case 'sqlite':
         case 'sqlite3':
             $convert->src_truncate_statement = 'DELETE FROM ';
             break;
             // Thanks MySQL, for silently converting...
         // Thanks MySQL, for silently converting...
         case 'mysql':
         case 'mysql4':
             if (version_compare($src_db->sql_server_info(true, false), '4.1.3', '>=')) {
                 $convert->mysql_convert = true;
             }
             $convert->src_truncate_statement = 'TRUNCATE TABLE ';
             break;
         case 'mysqli':
             $convert->mysql_convert = true;
             $convert->src_truncate_statement = 'TRUNCATE TABLE ';
             break;
         default:
             $convert->src_truncate_statement = 'TRUNCATE TABLE ';
             break;
     }
     if ($convert->mysql_convert && !$same_db) {
         $src_db->sql_query("SET NAMES 'binary'");
     }
     switch ($db->get_sql_layer()) {
         case 'sqlite':
         case 'sqlite3':
             $convert->truncate_statement = 'DELETE FROM ';
             break;
         default:
             $convert->truncate_statement = 'TRUNCATE TABLE ';
             break;
     }
     $get_info = false;
     // check security implications of direct inclusion
     if (!file_exists('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx)) {
         $this->error($user->lang['CONVERT_NOT_EXIST'], __LINE__, __FILE__);
     }
     if (file_exists('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx)) {
         include_once './convertors/functions_' . $convert->convertor_tag . '.' . $phpEx;
     }
     $get_info = true;
     include './convertors/convert_' . $convert->convertor_tag . '.' . $phpEx;
     // Map some variables...
     $convert->convertor_data = $convertor_data;
     $convert->tables = $tables;
     $convert->config_schema = $config_schema;
     // Now include the real data
     $get_info = false;
     include './convertors/convert_' . $convert->convertor_tag . '.' . $phpEx;
     $convert->convertor_data = $convertor_data;
     $convert->tables = $tables;
     $convert->config_schema = $config_schema;
     $convert->convertor = $convertor;
     // The test_file is a file that should be present in the location of the old board.
     if (!file_exists($convert->options['forum_path'] . '/' . $test_file)) {
         $this->error(sprintf($user->lang['COULD_NOT_FIND_PATH'], $convert->options['forum_path']), __LINE__, __FILE__);
     }
     $search_type = $config['search_type'];
     // For conversions we are a bit less strict and set to a search backend we know exist...
     if (!class_exists($search_type)) {
         $search_type = '\\phpbb\\search\\fulltext_native';
         $config->set('search_type', $search_type);
     }
     if (!class_exists($search_type)) {
         trigger_error('NO_SUCH_SEARCH_MODULE');
     }
     $error = false;
     $convert->fulltext_search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
     if ($error) {
         trigger_error($error);
     }
     include_once $phpbb_root_path . 'includes/message_parser.' . $phpEx;
     $message_parser = new \parse_message();
     $jump = $request->variable('jump', 0);
     $final_jump = $request->variable('final_jump', 0);
     $sync_batch = $request->variable('sync_batch', -1);
     $last_statement = $request->variable('last', 0);
     // We are running sync...
     if ($sync_batch >= 0) {
         $this->sync_forums($converter, $sync_batch);
         return;
     }
     if ($jump) {
         $this->jump($converter, $jump, $last_statement);
         return;
     }
     if ($final_jump) {
         $this->final_jump($final_jump);
         return;
     }
     $current_table = $request->variable('current_table', 0);
     $old_current_table = min(-1, $current_table - 1);
     $skip_rows = $request->variable('skip_rows', 0);
     if (!$current_table && !$skip_rows) {
         if (!$request->variable('confirm', false)) {
             // If avatars / ranks / smilies folders are specified make sure they are writable
             $bad_folders = array();
             $local_paths = array('avatar_path' => path($config['avatar_path']), 'avatar_gallery_path' => path($config['avatar_gallery_path']), 'icons_path' => path($config['icons_path']), 'ranks_path' => path($config['ranks_path']), 'smilies_path' => path($config['smilies_path']));
             foreach ($local_paths as $folder => $local_path) {
                 if (isset($convert->convertor[$folder])) {
                     if (empty($convert->convertor['test_file'])) {
                         // test_file is mandantory at the moment so this should never be reached, but just in case...
                         $this->error($user->lang['DEV_NO_TEST_FILE'], __LINE__, __FILE__);
                     }
                     if (!$local_path || !$this->filesystem->is_writable($phpbb_root_path . $local_path)) {
                         if (!$local_path) {
                             $bad_folders[] = sprintf($user->lang['CONFIG_PHPBB_EMPTY'], $folder);
                         } else {
                             $bad_folders[] = $local_path;
                         }
                     }
                 }
             }
             if (sizeof($bad_folders)) {
                 $msg = sizeof($bad_folders) == 1 ? $user->lang['MAKE_FOLDER_WRITABLE'] : $user->lang['MAKE_FOLDERS_WRITABLE'];
                 sort($bad_folders);
                 $this->error(sprintf($msg, implode('<br />', $bad_folders)), __LINE__, __FILE__, true);
                 $this->template->assign_vars(array('L_SUBMIT' => $user->lang['INSTALL_TEST'], 'U_ACTION' => $this->controller_helper->route('phpbb_convert_convert', array('converter' => $converter))));
                 return;
             }
             // Grab all the tables used in convertor
             $missing_tables = $tables_list = $aliases = array();
             foreach ($convert->convertor['schema'] as $schema) {
                 // Skip those not used (because of addons/plugins not detected)
                 if (!$schema['target']) {
                     continue;
                 }
                 foreach ($schema as $key => $val) {
                     // we're dealing with an array like:
                     // array('forum_status',			'forums.forum_status',				'is_item_locked')
                     if (is_int($key) && !empty($val[1])) {
                         $temp_data = $val[1];
                         if (!is_array($temp_data)) {
                             $temp_data = array($temp_data);
                         }
                         foreach ($temp_data as $value) {
                             if (preg_match('/([a-z0-9_]+)\\.([a-z0-9_]+)\\)* ?A?S? ?([a-z0-9_]*?)\\.?([a-z0-9_]*)$/i', $value, $m)) {
                                 $table = $convert->src_table_prefix . $m[1];
                                 $tables_list[$table] = $table;
                                 if (!empty($m[3])) {
                                     $aliases[] = $convert->src_table_prefix . $m[3];
                                 }
                             }
                         }
                     } else {
                         if ($key == 'left_join') {
                             // Convert the value if it wasn't an array already.
                             if (!is_array($val)) {
                                 $val = array($val);
                             }
                             for ($j = 0; $j < sizeof($val); ++$j) {
                                 if (preg_match('/LEFT JOIN ([a-z0-9_]+) AS ([a-z0-9_]+)/i', $val[$j], $m)) {
                                     $table = $convert->src_table_prefix . $m[1];
                                     $tables_list[$table] = $table;
                                     if (!empty($m[2])) {
                                         $aliases[] = $convert->src_table_prefix . $m[2];
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
             // Remove aliased tables from $tables_list
             foreach ($aliases as $alias) {
                 unset($tables_list[$alias]);
             }
             // Check if the tables that we need exist
             $src_db->sql_return_on_error(true);
             foreach ($tables_list as $table => $null) {
                 $sql = 'SELECT 1 FROM ' . $table;
                 $_result = $src_db->sql_query_limit($sql, 1);
                 if (!$_result) {
                     $missing_tables[] = $table;
                 }
                 $src_db->sql_freeresult($_result);
             }
             $src_db->sql_return_on_error(false);
             // Throw an error if some tables are missing
             // We used to do some guessing here, but since we have a suggestion of possible values earlier, I don't see it adding anything here to do it again
             if (sizeof($missing_tables) == sizeof($tables_list)) {
                 $this->error($user->lang['NO_TABLES_FOUND'] . ' ' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
             } else {
                 if (sizeof($missing_tables)) {
                     $this->error(sprintf($user->lang['TABLES_MISSING'], implode($user->lang['COMMA_SEPARATOR'], $missing_tables)) . '<br /><br />' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
                 }
             }
             $url = $this->save_convert_progress($converter, 'confirm=1');
             $msg = $user->lang['PRE_CONVERT_COMPLETE'];
             if ($convert->convertor_data['author_notes']) {
                 $msg .= '</p><p>' . sprintf($user->lang['AUTHOR_NOTES'], $convert->convertor_data['author_notes']);
             }
             $this->template->assign_vars(array('L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], 'BODY' => $msg, 'U_ACTION' => $url));
             return;
         }
         // if (!$request->variable('confirm', false)))
         $this->template->assign_block_vars('checks', array('S_LEGEND' => true, 'LEGEND' => $user->lang['STARTING_CONVERT']));
         // Convert the config table and load the settings of the old board
         if (!empty($convert->config_schema)) {
             restore_config($convert->config_schema);
             // Override a couple of config variables for the duration
             $config['max_quote_depth'] = 0;
             // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues
             $config['max_post_chars'] = $config['min_post_chars'] = 0;
         }
         $this->template->assign_block_vars('checks', array('TITLE' => $user->lang['CONFIG_CONVERT'], 'RESULT' => $user->lang['DONE']));
         // Now process queries and execute functions that have to be executed prior to the conversion
         if (!empty($convert->convertor['execute_first'])) {
             // @codingStandardsIgnoreStart
             eval($convert->convertor['execute_first']);
             // @codingStandardsIgnoreEnd
         }
         if (!empty($convert->convertor['query_first'])) {
             if (!is_array($convert->convertor['query_first'])) {
                 $convert->convertor['query_first'] = array('target', array($convert->convertor['query_first']));
             } else {
                 if (!is_array($convert->convertor['query_first'][0])) {
                     $convert->convertor['query_first'] = array(array($convert->convertor['query_first'][0], $convert->convertor['query_first'][1]));
                 }
             }
             foreach ($convert->convertor['query_first'] as $query_first) {
                 if ($query_first[0] == 'src') {
                     if ($convert->mysql_convert && $same_db) {
                         $src_db->sql_query("SET NAMES 'binary'");
                     }
                     $src_db->sql_query($query_first[1]);
                     if ($convert->mysql_convert && $same_db) {
                         $src_db->sql_query("SET NAMES 'utf8'");
                     }
                 } else {
                     $db->sql_query($query_first[1]);
                 }
             }
         }
         $this->template->assign_block_vars('checks', array('TITLE' => $user->lang['PREPROCESS_STEP'], 'RESULT' => $user->lang['DONE']));
     }
     // if (!$current_table && !$skip_rows)
     $this->template->assign_block_vars('checks', array('S_LEGEND' => true, 'LEGEND' => $user->lang['FILLING_TABLES']));
     // This loop takes one target table and processes it
     while ($current_table < sizeof($convert->convertor['schema'])) {
         $schema = $convert->convertor['schema'][$current_table];
         // The target table isn't set, this can be because a module (for example the attachement mod) is taking care of this.
         if (empty($schema['target'])) {
             $current_table++;
             continue;
         }
         $this->template->assign_block_vars('checks', array('TITLE' => sprintf($user->lang['FILLING_TABLE'], $schema['target'])));
         // This is only the case when we first start working on the tables.
         if (!$skip_rows) {
             // process execute_first and query_first for this table...
             if (!empty($schema['execute_first'])) {
                 // @codingStandardsIgnoreStart
                 eval($schema['execute_first']);
                 // @codingStandardsIgnoreEnd
             }
             if (!empty($schema['query_first'])) {
                 if (!is_array($schema['query_first'])) {
                     $schema['query_first'] = array('target', array($schema['query_first']));
                 } else {
                     if (!is_array($schema['query_first'][0])) {
                         $schema['query_first'] = array(array($schema['query_first'][0], $schema['query_first'][1]));
                     }
                 }
                 foreach ($schema['query_first'] as $query_first) {
                     if ($query_first[0] == 'src') {
                         if ($convert->mysql_convert && $same_db) {
                             $src_db->sql_query("SET NAMES 'binary'");
                         }
                         $src_db->sql_query($query_first[1]);
                         if ($convert->mysql_convert && $same_db) {
                             $src_db->sql_query("SET NAMES 'utf8'");
                         }
                     } else {
                         $db->sql_query($query_first[1]);
                     }
                 }
             }
             if (!empty($schema['autoincrement'])) {
                 switch ($db->get_sql_layer()) {
                     case 'postgres':
                         $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));');
                         break;
                     case 'oracle':
                         $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']);
                         $row = $db->sql_fetchrow($result);
                         $db->sql_freeresult($result);
                         $largest_id = (int) $row['max_id'];
                         if ($largest_id) {
                             $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq');
                             $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1));
                         }
                         break;
                 }
             }
         }
         // Process execute_always for this table
         // This is for code which needs to be executed on every pass of this table if
         // it gets split because of time restrictions
         if (!empty($schema['execute_always'])) {
             // @codingStandardsIgnoreStart
             eval($schema['execute_always']);
             // @codingStandardsIgnoreEnd
         }
         //
         // Set up some variables
         //
         // $waiting_rows	holds rows for multirows insertion (MySQL only)
         // $src_tables		holds unique tables with aliases to select from
         // $src_fields		will quickly refer source fields (or aliases) corresponding to the current index
         // $select_fields	holds the names of the fields to retrieve
         //
         $sql_data = array('source_fields' => array(), 'target_fields' => array(), 'source_tables' => array(), 'select_fields' => array());
         // This statement is building the keys for later insertion.
         $insert_query = $this->build_insert_query($schema, $sql_data, $current_table);
         // If no source table is affected, we skip the table
         if (empty($sql_data['source_tables'])) {
             $skip_rows = 0;
             $current_table++;
             continue;
         }
         $distinct = !empty($schema['distinct']) ? 'DISTINCT ' : '';
         $sql = 'SELECT ' . $distinct . implode(', ', $sql_data['select_fields']) . " \nFROM " . implode(', ', $sql_data['source_tables']);
         // Where
         $sql .= !empty($schema['where']) ? "\nWHERE (" . $schema['where'] . ')' : '';
         // Group By
         if (!empty($schema['group_by'])) {
             $schema['group_by'] = array($schema['group_by']);
             foreach ($sql_data['select_fields'] as $select) {
                 $alias = strpos(strtolower($select), ' as ');
                 $select = $alias ? substr($select, 0, $alias) : $select;
                 if (!in_array($select, $schema['group_by'])) {
                     $schema['group_by'][] = $select;
                 }
             }
         }
         $sql .= !empty($schema['group_by']) ? "\nGROUP BY " . implode(', ', $schema['group_by']) : '';
         // Having
         $sql .= !empty($schema['having']) ? "\nHAVING " . $schema['having'] : '';
         // Order By
         if (empty($schema['order_by']) && !empty($schema['primary'])) {
             $schema['order_by'] = $schema['primary'];
         }
         $sql .= !empty($schema['order_by']) ? "\nORDER BY " . $schema['order_by'] : '';
         // Counting basically holds the amount of rows processed.
         $counting = -1;
         $batch_time = 0;
         while ($counting === -1 || $counting >= $convert->batch_size && still_on_time()) {
             $old_current_table = $current_table;
             $rows = '';
             $waiting_rows = array();
             if (!empty($batch_time)) {
                 $mtime = explode(' ', microtime());
                 $mtime = $mtime[0] + $mtime[1];
                 $rows = ceil($counting / ($mtime - $batch_time)) . " rows/s ({$counting} rows) | ";
             }
             $this->template->assign_block_vars('checks', array('TITLE' => "skip_rows = {$skip_rows}", 'RESULT' => $rows . (defined('DEBUG') && function_exists('memory_get_usage') ? ceil(memory_get_usage() / 1024) . ' ' . $user->lang['KIB'] : '')));
             $mtime = explode(' ', microtime());
             $batch_time = $mtime[0] + $mtime[1];
             if ($convert->mysql_convert && $same_db) {
                 $src_db->sql_query("SET NAMES 'binary'");
             }
             // Take skip rows into account and only fetch batch_size amount of rows
             $___result = $src_db->sql_query_limit($sql, $convert->batch_size, $skip_rows);
             if ($convert->mysql_convert && $same_db) {
                 $src_db->sql_query("SET NAMES 'utf8'");
             }
             // This loop processes each row
             $counting = 0;
             $convert->row = $convert_row = array();
             if (!empty($schema['autoincrement'])) {
                 switch ($db->get_sql_layer()) {
                     case 'mssql':
                     case 'mssql_odbc':
                     case 'mssqlnative':
                         $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' ON');
                         break;
                 }
             }
             // Now handle the rows until time is over or no more rows to process...
             while ($counting === 0 || still_on_time()) {
                 $convert_row = $src_db->sql_fetchrow($___result);
                 if (!$convert_row) {
                     // move to the next batch or table
                     break;
                 }
                 // With this we are able to always save the last state
                 $convert->row = $convert_row;
                 // Increment the counting variable, it stores the number of rows we have processed
                 $counting++;
                 $insert_values = array();
                 $sql_flag = $this->process_row($schema, $sql_data, $insert_values);
                 if ($sql_flag === true) {
                     switch ($db->get_sql_layer()) {
                         // If MySQL, we'll wait to have num_wait_rows rows to submit at once
                         case 'mysql':
                         case 'mysql4':
                         case 'mysqli':
                             $waiting_rows[] = '(' . implode(', ', $insert_values) . ')';
                             if (sizeof($waiting_rows) >= $convert->num_wait_rows) {
                                 $errored = false;
                                 $db->sql_return_on_error(true);
                                 if (!$db->sql_query($insert_query . implode(', ', $waiting_rows))) {
                                     $errored = true;
                                 }
                                 $db->sql_return_on_error(false);
                                 if ($errored) {
                                     $db->sql_return_on_error(true);
                                     // Because it errored out we will try to insert the rows one by one... most of the time this
                                     // is caused by duplicate entries - but we also do not want to miss one...
                                     foreach ($waiting_rows as $waiting_sql) {
                                         if (!$db->sql_query($insert_query . $waiting_sql)) {
                                             $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
                                         }
                                     }
                                     $db->sql_return_on_error(false);
                                 }
                                 $waiting_rows = array();
                             }
                             break;
                         default:
                             $insert_sql = $insert_query . '(' . implode(', ', $insert_values) . ')';
                             $db->sql_return_on_error(true);
                             if (!$db->sql_query($insert_sql)) {
                                 $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
                             }
                             $db->sql_return_on_error(false);
                             $waiting_rows = array();
                             break;
                     }
                 }
                 $skip_rows++;
             }
             $src_db->sql_freeresult($___result);
             // We might still have some rows waiting
             if (sizeof($waiting_rows)) {
                 $errored = false;
                 $db->sql_return_on_error(true);
                 if (!$db->sql_query($insert_query . implode(', ', $waiting_rows))) {
                     $errored = true;
                 }
                 $db->sql_return_on_error(false);
                 if ($errored) {
                     $db->sql_return_on_error(true);
                     // Because it errored out we will try to insert the rows one by one... most of the time this
                     // is caused by duplicate entries - but we also do not want to miss one...
                     foreach ($waiting_rows as $waiting_sql) {
                         $db->sql_query($insert_query . $waiting_sql);
                         $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
                     }
                     $db->sql_return_on_error(false);
                 }
                 $waiting_rows = array();
             }
             if (!empty($schema['autoincrement'])) {
                 switch ($db->get_sql_layer()) {
                     case 'mssql':
                     case 'mssql_odbc':
                     case 'mssqlnative':
                         $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' OFF');
                         break;
                     case 'postgres':
                         $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));');
                         break;
                     case 'oracle':
                         $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']);
                         $row = $db->sql_fetchrow($result);
                         $db->sql_freeresult($result);
                         $largest_id = (int) $row['max_id'];
                         if ($largest_id) {
                             $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq');
                             $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1));
                         }
                         break;
                 }
             }
         }
         // When we reach this point, either the current table has been processed or we're running out of time.
         if (still_on_time() && $counting < $convert->batch_size) {
             $skip_rows = 0;
             $current_table++;
         } else {
             /*
             				if (still_on_time() && $counting < $convert->batch_size)
             				{
             					$skip_rows = 0;
             					$current_table++;
             				}*/
             // Looks like we ran out of time.
             $url = $this->save_convert_progress($converter, 'current_table=' . $current_table . '&amp;skip_rows=' . $skip_rows);
             $current_table++;
             //				$percentage = ($skip_rows == 0) ? 0 : floor(100 / ($total_rows / $skip_rows));
             $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $current_table, sizeof($convert->convertor['schema']));
             $this->template->assign_vars(array('BODY' => $msg, 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], 'U_ACTION' => $url));
             $this->meta_refresh($url);
             return;
         }
     }
     // Process execute_last then we'll be done
     $url = $this->save_convert_progress($converter, 'jump=1');
     $this->template->assign_vars(array('L_SUBMIT' => $user->lang['FINAL_STEP'], 'U_ACTION' => $url));
     $this->meta_refresh($url);
     return;
 }