<?php
/**
* This file implements upgrading of DB tables
*
* b2evolution - {@link http://b2evolution.net/}
* Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
*
* @package install
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
load_funcs( '_core/_param.funcs.php' );
load_funcs( 'widgets/_widgets.funcs.php' );
/**
* Create a DB version checkpoint
*
* This is useful when the next operation might timeout or fail!
* The checkpoint will allow to restart the script and continue where it stopped
*
* @param string version of DB at checkpoint
*/
function set_upgrade_checkpoint( $version )
{
global $DB, $script_start_time, $locale, $action;
global $recreate_autogenerated_excerpts;
echo get_install_format_text_and_log( '<span class="text-muted">Creating DB schema version checkpoint at '.$version.'... ' );
if( $version < 8060 )
{
$query = 'UPDATE T_settings SET db_version = '.$version;
}
else
{
$query = "UPDATE T_settings
SET set_value = '$version'
WHERE set_name = 'db_version'";
}
$DB->query( $query );
$elapsed_time = time() - $script_start_time;
echo get_install_format_text_and_log( "OK. (Elapsed upgrade time: $elapsed_time seconds)</span><br />\n", 'br' );
// Update the progress bar status
update_install_progress_bar();
evo_flush();
$max_exe_time = ini_get( 'max_execution_time' );
if( $max_exe_time && ( $elapsed_time > ( $max_exe_time - 20 ) ) )
{ // Max exe time not disabled and we're recahing the end
$upgrade_action = ( $action == 'auto_upgrade' ) ? $action : 'evoupgrade';
$recreate_excerpts = $recreate_autogenerated_excerpts ? '&recreate_excerpts=1' : '';
echo get_install_format_text_and_log( 'We are reaching the time limit for this script. Please click <a href="index.php?locale='.$locale.'&action='.$upgrade_action.$recreate_excerpts.'">continue</a>...' );
// Dirty temporary solution:
exit(0);
}
}
/**
* Start upgrade task
*
* @param integer Version of DB at checkpoint
* @param string Task title
* @return boolean TRUE if task has been started
*/
function upg_task_start( $version, $title = '' )
{
global $old_db_version, $upgrade_db_version;
if( $old_db_version < $version )
{ // Only if current DB version is older than current requested
// Set upgrade version to use this in function upg_task_end():
$upgrade_db_version = $version;
if( ! empty( $title ) )
{ // Start task (Display a title):
task_begin( $title );
}
// Begin a transaction for the upgrade block:
global $DB;
$DB->begin();
return true;
}
else
{
return false;
}
}
/**
* End upgrade task
*
* @param boolean TRUE to print out a result of task ending
*/
function upg_task_end( $print_result = true )
{
global $upgrade_db_version;
if( ! empty( $upgrade_db_version ) )
{ // Only if the upgrade task is not ended yet:
// End current transaction of the upgrade block:
global $DB;
$DB->commit();
if( $print_result )
{ // Print out the end of task:
task_end();
}
// Create a DB version checkpoint:
set_upgrade_checkpoint( $upgrade_db_version );
// Unset the current version because task is ended:
unset( $upgrade_db_version );
}
}
/**
* Create DB table
*
* @param string Table name
* @param string Fields
* @param string Options
*/
function db_create_table( $table, $fields, $options = 'ENGINE = innodb' )
{
global $DB, $db_config;
if( isset( $db_config, $db_config['aliases'], $db_config['aliases'][ $table ] ) )
{ // Use table name with real prefix instead of T_:
$table = $db_config['aliases'][ $table ];
}
// Check if the creating table doesn't exist in DB:
$unique_table = $table;
$next_table_index = 1;
while( $DB->get_var( 'SHOW TABLES LIKE '.$DB->quote( $unique_table ) ) )
{ // This table already exists in DB, try to find what next table name is free:
$unique_table = $table.'_'.$next_table_index;
$next_table_index++;
}
if( $unique_table != $table )
{ // Backup the existing table, in order to create the requeired table without error:
$DB->query( 'RENAME TABLE '.$table.' TO '.$unique_table );
}
// Create new table with requested table:
$DB->query( 'CREATE TABLE '.$table.' ( '.$fields.' ) '.$options );
}
/**
* @return boolean Does a given index key name exist in DB?
*/
function db_index_exists( $table, $index_name )
{
global $DB;
$index_name = strtolower($index_name);
$DB->query('SHOW INDEX FROM '.$table);
while( $row = $DB->get_row() )
{
if( strtolower($row->Key_name) == $index_name )
{
return true;
}
}
return false;
}
/**
* @param string Table name
* @param array Column names
* @return boolean Does a list of given column names exist in DB?
*/
function db_cols_exist( $table, $col_names )
{
global $DB;
foreach( $col_names as $k => $v )
$col_names[$k] = strtolower($v);
foreach( $DB->get_results('SHOW COLUMNS FROM '.$table) as $row )
if( ($key = array_search(strtolower($row->Field), $col_names)) !== false )
unset( $col_names[$key] );
return count($col_names) == 0;
}
/**
* Drops a column, if it exists.
*/
function db_drop_col( $table, $col_name )
{
global $DB;
if( ! db_col_exists($table, $col_name) )
return false;
$DB->query( 'ALTER TABLE '.$table.' DROP COLUMN '.$col_name );
}
/**
* Add a column, if it does not already exist.
* If it exists already, a "ALTER TABLE" statement will get executed instead.
*
* @return boolean True if the column has been added, False if not.
*/
function db_add_col( $table, $col_name, $col_desc )
{
global $DB;
if( db_col_exists( $table, $col_name ) )
{ // Column exists already, make sure it's the same.
db_modify_col( $table, $col_name, $col_desc );
return false;
}
$DB->query( 'ALTER TABLE '.$table.' ADD COLUMN '.$col_name.' '.$col_desc );
}
/**
* Modify a column
*/
function db_modify_col( $table, $col_name, $col_desc )
{
global $DB;
$DB->query( 'ALTER TABLE '.$table.' MODIFY COLUMN '.$col_name.' '.$col_desc );
}
/**
* ADD/MODIFY/DROP columns by single SQL query
*
* @param string Table name
* @param array Array: key - action('ADD','MODIFY','DROP'),
* value is array of columns: key - column name, value - column description or column name for DROP action
*/
function db_upgrade_cols( $table, $cols )
{
global $DB;
if( empty( $cols ) )
{ // No columns
return;
}
// Get existing columns of the given db table in order to avoid errors on add duplicated column or on drop unexisting column:
$existing_columns = $DB->get_results( 'SHOW COLUMNS FROM '.$table );
foreach( $existing_columns as $c => $col )
{
$existing_columns[ $c ] = strtolower( $col->Field );
}
$upgrade_sql_query = '';
foreach( $cols as $action => $cols_data )
{
foreach( $cols_data as $col_name => $col_desc )
{
switch( $action )
{
case 'ADD':
case 'MODIFY':
case 'CHANGE':
if( in_array( strtolower( $col_name ), $existing_columns ) )
{ // Modify the existing column:
if( $action == 'CHANGE' )
{ // Change a column, e-g rename it:
$upgrade_sql_query .= ' CHANGE COLUMN ';
}
else
{ // Modify a column, update only
$upgrade_sql_query .= ' MODIFY COLUMN ';
}
}
else
{ // Add new column:
$upgrade_sql_query .= ' ADD COLUMN ';
}
$upgrade_sql_query .= '`'.$col_name.'` '.$col_desc.',';
break;
case 'DROP':
$col_name = $col_desc;
if( in_array( strtolower( $col_name ), $existing_columns ) )
{ // Allow to drop only really existing column:
$upgrade_sql_query .= ' DROP COLUMN `'.$col_name.'`,';
}
// ELSE skip not existing column
break;
default:
debug_die( 'Invalid DB upgrade action "'.$action.'"' );
}
}
}
if( ! empty( $upgrade_sql_query ) )
{ // Run upgrade SQL query only if at least one column should be upgraded:
$DB->query( 'ALTER