diff options
author | Nicofuma <github@nicofuma.fr> | 2015-02-09 17:11:09 +0100 |
---|---|---|
committer | Nicofuma <github@nicofuma.fr> | 2015-02-09 17:11:09 +0100 |
commit | acf3744297303324490c484156f0bf808e532883 (patch) | |
tree | 5dfae513699ce84bbdf0028842732695df7c7e47 | |
parent | b0c910e49c31e086f19f983bb9d7d5051d34fac8 (diff) | |
parent | 74d1b1812f512ae232a063a2574d62d3f5491f46 (diff) | |
download | forums-acf3744297303324490c484156f0bf808e532883.tar forums-acf3744297303324490c484156f0bf808e532883.tar.gz forums-acf3744297303324490c484156f0bf808e532883.tar.bz2 forums-acf3744297303324490c484156f0bf808e532883.tar.xz forums-acf3744297303324490c484156f0bf808e532883.zip |
Merge pull request #3332 from nickvergessen/ticket/10748-mssql
[ticket/10748] Split MS SQL from the db tools file into it's own
22 files changed, 922 insertions, 605 deletions
diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index 5c5bfe6171..8dce97144d 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -49,7 +49,10 @@ $classes = $finder->core_path('phpbb/') ->get_classes(); $db = new \phpbb\db\driver\sqlite(); -$schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, new \phpbb\db\tools\tools($db, true), $phpbb_root_path, $phpEx, $table_prefix); +$factory = new \phpbb\db\tools\factory(); +$db_tools = $factory->get($db, true); + +$schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, $db_tools, $phpbb_root_path, $phpEx, $table_prefix); $schema_data = $schema_generator->get_schema(); $fp = fopen($schema_path . 'schema.json', 'wb'); diff --git a/phpBB/develop/mysql_upgrader.php b/phpBB/develop/mysql_upgrader.php index 51fbd75c10..543b520caa 100644 --- a/phpBB/develop/mysql_upgrader.php +++ b/phpBB/develop/mysql_upgrader.php @@ -67,7 +67,10 @@ $classes = $finder->core_path('phpbb/') ->directory('/db/migration/data') ->get_classes(); -$schema_generator = new \phpbb\db\migration\schema_generator($classes, $config, $db, new \phpbb\db\tools\tools($db, true), $phpbb_root_path, $phpEx, $table_prefix); +$factory = new \phpbb\db\tools\factory(); +$db_tools = $factory->get($db, true); + +$schema_generator = new \phpbb\db\migration\schema_generator($classes, $config, $db, $db_tools, $phpbb_root_path, $phpEx, $table_prefix); $schema_data = $schema_generator->get_schema(); $dbms_type_map = \phpbb\db\tools\tools::get_dbms_type_map(); diff --git a/phpBB/includes/acp/acp_database.php b/phpBB/includes/acp/acp_database.php index fb02e58b13..25cddaa5d4 100644 --- a/phpBB/includes/acp/acp_database.php +++ b/phpBB/includes/acp/acp_database.php @@ -26,10 +26,10 @@ class acp_database function main($id, $mode) { - global $cache, $db, $user, $auth, $template, $table_prefix, $request; - global $config, $phpbb_root_path, $phpbb_admin_path, $phpEx, $phpbb_log; + global $cache, $db, $user, $template, $table_prefix, $request; + global $phpbb_root_path, $phpbb_container, $phpbb_log; - $this->db_tools = new \phpbb\db\tools\tools($db); + $this->db_tools = $phpbb_container->get('dbal.tools'); $user->add_lang('acp/database'); diff --git a/phpBB/includes/functions_install.php b/phpBB/includes/functions_install.php index 956e5a5180..4217617b53 100644 --- a/phpBB/includes/functions_install.php +++ b/phpBB/includes/functions_install.php @@ -188,7 +188,8 @@ function dbms_select($default = '', $only_20x_options = false) */ function get_tables(&$db) { - $db_tools = new \phpbb\db\tools\tools($db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db); return $db_tools->sql_list_tables(); } diff --git a/phpBB/install/convertors/functions_phpbb20.php b/phpBB/install/convertors/functions_phpbb20.php index f8a6954634..48cff426b8 100644 --- a/phpBB/install/convertors/functions_phpbb20.php +++ b/phpBB/install/convertors/functions_phpbb20.php @@ -1926,7 +1926,9 @@ function phpbb_check_username_collisions() function phpbb_convert_timezone($timezone) { global $config, $db, $phpbb_root_path, $phpEx, $table_prefix; - $timezone_migration = new \phpbb\db\migration\data\v310\timezone($config, $db, new \phpbb\db\tools\tools($db), $phpbb_root_path, $phpEx, $table_prefix); + + $factory = new \phpbb\db\tools\factory(); + $timezone_migration = new \phpbb\db\migration\data\v310\timezone($config, $db, $factory->get($db), $phpbb_root_path, $phpEx, $table_prefix); return $timezone_migration->convert_phpbb30_timezone($timezone, 0); } diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 380f34bab6..e38c9c5bdf 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1200,7 +1200,9 @@ class install_install extends module ->get_classes(); $sqlite_db = new \phpbb\db\driver\sqlite(); - $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $sqlite_db, new \phpbb\db\tools\tools($sqlite_db, true), $phpbb_root_path, $phpEx, $table_prefix); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($sqlite_db, true); + $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $sqlite_db, $db_tools, $phpbb_root_path, $phpEx, $table_prefix); $db_table_schema = $schema_generator->get_schema(); } @@ -1212,7 +1214,8 @@ class install_install extends module define('CONFIG_TABLE', $data['table_prefix'] . 'config'); } - $db_tools = new \phpbb\db\tools\tools($db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db); foreach ($db_table_schema as $table_name => $table_data) { $db_tools->sql_create_table( diff --git a/phpBB/phpbb/captcha/plugins/qa.php b/phpBB/phpbb/captcha/plugins/qa.php index b4586f1234..cf03f92e96 100644 --- a/phpBB/phpbb/captcha/plugins/qa.php +++ b/phpBB/phpbb/captcha/plugins/qa.php @@ -113,9 +113,9 @@ class qa */ public function is_installed() { - global $db; + global $phpbb_container; - $db_tool = new \phpbb\db\tools\tools($db); + $db_tool = $phpbb_container->get('dbal.tools'); return $db_tool->sql_table_exists($this->table_captcha_questions); } @@ -306,11 +306,9 @@ class qa */ function install() { - global $db; - - $db_tool = new \phpbb\db\tools\tools($db); + global $phpbb_container; - $tables = array($this->table_captcha_questions, $this->table_captcha_answers, $this->table_qa_confirm); + $db_tool = $phpbb_container->get('dbal.tools'); $schemas = array( $this->table_captcha_questions => array ( @@ -352,7 +350,7 @@ class qa ), ); - foreach($schemas as $table => $schema) + foreach ($schemas as $table => $schema) { if (!$db_tool->sql_table_exists($table)) { diff --git a/phpBB/phpbb/db/tools/factory.php b/phpBB/phpbb/db/tools/factory.php index 60db2ade03..24e2a4d875 100644 --- a/phpBB/phpbb/db/tools/factory.php +++ b/phpBB/phpbb/db/tools/factory.php @@ -25,7 +25,11 @@ class factory */ public function get($db_driver, $return_statements = false) { - if ($db_driver instanceof \phpbb\db\driver\driver_interface) + if ($db_driver instanceof \phpbb\db\driver\mssql || $db_driver instanceof \phpbb\db\driver\mssql_base) + { + return new \phpbb\db\tools\mssql($db_driver, $return_statements); + } + else if ($db_driver instanceof \phpbb\db\driver\driver_interface) { return new \phpbb\db\tools\tools($db_driver, $return_statements); } diff --git a/phpBB/phpbb/db/tools/mssql.php b/phpBB/phpbb/db/tools/mssql.php new file mode 100644 index 0000000000..3a7ac6185d --- /dev/null +++ b/phpBB/phpbb/db/tools/mssql.php @@ -0,0 +1,793 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\tools; + +/** +* Database Tools for handling cross-db actions such as altering columns, etc. +* Currently not supported is returning SQL for creating tables. +*/ +class mssql extends tools +{ + /** + * Is the used MS SQL Server a SQL Server 2000? + * @var bool + */ + protected $is_sql_server_2000; + + /** + * Get the column types for every database we support + * + * @return array + */ + public static function get_dbms_type_map() + { + return array( + 'mssql' => array( + 'INT:' => '[int]', + 'BINT' => '[float]', + 'UINT' => '[int]', + 'UINT:' => '[int]', + 'TINT:' => '[int]', + 'USINT' => '[int]', + 'BOOL' => '[int]', + 'VCHAR' => '[varchar] (255)', + 'VCHAR:' => '[varchar] (%d)', + 'CHAR:' => '[char] (%d)', + 'XSTEXT' => '[varchar] (1000)', + 'STEXT' => '[varchar] (3000)', + 'TEXT' => '[varchar] (8000)', + 'MTEXT' => '[text]', + 'XSTEXT_UNI'=> '[varchar] (100)', + 'STEXT_UNI' => '[varchar] (255)', + 'TEXT_UNI' => '[varchar] (4000)', + 'MTEXT_UNI' => '[text]', + 'TIMESTAMP' => '[int]', + 'DECIMAL' => '[float]', + 'DECIMAL:' => '[float]', + 'PDECIMAL' => '[float]', + 'PDECIMAL:' => '[float]', + 'VCHAR_UNI' => '[varchar] (255)', + 'VCHAR_UNI:'=> '[varchar] (%d)', + 'VCHAR_CI' => '[varchar] (255)', + 'VARBINARY' => '[varchar] (255)', + ), + + 'mssqlnative' => array( + 'INT:' => '[int]', + 'BINT' => '[float]', + 'UINT' => '[int]', + 'UINT:' => '[int]', + 'TINT:' => '[int]', + 'USINT' => '[int]', + 'BOOL' => '[int]', + 'VCHAR' => '[varchar] (255)', + 'VCHAR:' => '[varchar] (%d)', + 'CHAR:' => '[char] (%d)', + 'XSTEXT' => '[varchar] (1000)', + 'STEXT' => '[varchar] (3000)', + 'TEXT' => '[varchar] (8000)', + 'MTEXT' => '[text]', + 'XSTEXT_UNI'=> '[varchar] (100)', + 'STEXT_UNI' => '[varchar] (255)', + 'TEXT_UNI' => '[varchar] (4000)', + 'MTEXT_UNI' => '[text]', + 'TIMESTAMP' => '[int]', + 'DECIMAL' => '[float]', + 'DECIMAL:' => '[float]', + 'PDECIMAL' => '[float]', + 'PDECIMAL:' => '[float]', + 'VCHAR_UNI' => '[varchar] (255)', + 'VCHAR_UNI:'=> '[varchar] (%d)', + 'VCHAR_CI' => '[varchar] (255)', + 'VARBINARY' => '[varchar] (255)', + ), + ); + } + + /** + * Constructor. Set DB Object and set {@link $return_statements return_statements}. + * + * @param \phpbb\db\driver\driver_interface $db Database connection + * @param bool $return_statements True if only statements should be returned and no SQL being executed + */ + public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false) + { + parent::__construct($db, $return_statements); + + // Determine mapping database type + switch ($this->db->get_sql_layer()) + { + case 'mssql': + case 'mssql_odbc': + $this->sql_layer = 'mssql'; + break; + + case 'mssqlnative': + $this->sql_layer = 'mssqlnative'; + break; + } + + $this->dbms_type_map = self::get_dbms_type_map(); + } + + /** + * {@inheritDoc} + */ + function sql_list_tables() + { + $sql = "SELECT name + FROM sysobjects + WHERE type='U'"; + $result = $this->db->sql_query($sql); + + $tables = array(); + while ($row = $this->db->sql_fetchrow($result)) + { + $name = current($row); + $tables[$name] = $name; + } + $this->db->sql_freeresult($result); + + return $tables; + } + + /** + * {@inheritDoc} + */ + function sql_create_table($table_name, $table_data) + { + // holds the DDL for a column + $columns = $statements = array(); + + if ($this->sql_table_exists($table_name)) + { + return $this->_sql_run_sql($statements); + } + + // Begin transaction + $statements[] = 'begin'; + + // Determine if we have created a PRIMARY KEY in the earliest + $primary_key_gen = false; + + // Determine if the table requires a sequence + $create_sequence = false; + + // Begin table sql statement + $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n"; + + if (!isset($table_data['PRIMARY_KEY'])) + { + $table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment'); + $table_data['PRIMARY_KEY'] = 'mssqlindex'; + } + + // Iterate through the columns to create a table + foreach ($table_data['COLUMNS'] as $column_name => $column_data) + { + // here lies an array, filled with information compiled on the column's data + $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data); + + if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen" + { + trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR); + } + + // here we add the definition of the new column to the list of columns + $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default']; + + // see if we have found a primary key set due to a column definition if we have found it, we can stop looking + if (!$primary_key_gen) + { + $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set']; + } + + // create sequence DDL based off of the existance of auto incrementing columns + if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment']) + { + $create_sequence = $column_name; + } + } + + // this makes up all the columns in the create table statement + $table_sql .= implode(",\n", $columns); + + // Close the table for two DBMS and add to the statements + $table_sql .= "\n);"; + $statements[] = $table_sql; + + // we have yet to create a primary key for this table, + // this means that we can add the one we really wanted instead + if (!$primary_key_gen) + { + // Write primary key + if (isset($table_data['PRIMARY_KEY'])) + { + if (!is_array($table_data['PRIMARY_KEY'])) + { + $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']); + } + + // We need the data here + $old_return_statements = $this->return_statements; + $this->return_statements = true; + + $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']); + foreach ($primary_key_stmts as $pk_stmt) + { + $statements[] = $pk_stmt; + } + + $this->return_statements = $old_return_statements; + } + } + + // Write Keys + if (isset($table_data['KEYS'])) + { + foreach ($table_data['KEYS'] as $key_name => $key_data) + { + if (!is_array($key_data[1])) + { + $key_data[1] = array($key_data[1]); + } + + $old_return_statements = $this->return_statements; + $this->return_statements = true; + + $key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]); + + foreach ($key_stmts as $key_stmt) + { + $statements[] = $key_stmt; + } + + $this->return_statements = $old_return_statements; + } + } + + // Commit Transaction + $statements[] = 'commit'; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_list_columns($table_name) + { + $columns = array(); + + $sql = "SELECT c.name + FROM syscolumns c + LEFT JOIN sysobjects o ON c.id = o.id + WHERE o.name = '{$table_name}'"; + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $column = strtolower(current($row)); + $columns[$column] = $column; + } + $this->db->sql_freeresult($result); + + return $columns; + } + + /** + * {@inheritDoc} + */ + function sql_index_exists($table_name, $index_name) + { + $sql = "EXEC sp_statistics '$table_name'"; + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + if ($row['TYPE'] == 3) + { + if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) + { + $this->db->sql_freeresult($result); + return true; + } + } + } + $this->db->sql_freeresult($result); + + return false; + } + + /** + * {@inheritDoc} + */ + function sql_unique_index_exists($table_name, $index_name) + { + $sql = "EXEC sp_statistics '$table_name'"; + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + // Usually NON_UNIQUE is the column we want to check, but we allow for both + if ($row['TYPE'] == 3) + { + if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) + { + $this->db->sql_freeresult($result); + return true; + } + } + } + $this->db->sql_freeresult($result); + + return false; + } + + /** + * {@inheritDoc} + */ + function sql_prepare_column_data($table_name, $column_name, $column_data) + { + if (strlen($column_name) > 30) + { + trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR); + } + + // Get type + list($column_type, ) = $this->get_column_type($column_data[0]); + + // Adjust default value if db-dependent specified + if (is_array($column_data[1])) + { + $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default']; + } + + $sql = ''; + + $return_array = array(); + + $sql .= " {$column_type} "; + $sql_default = " {$column_type} "; + + // For adding columns we need the default definition + if (!is_null($column_data[1])) + { + // For hexadecimal values do not use single quotes + if (strpos($column_data[1], '0x') === 0) + { + $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') '; + $sql_default .= $return_array['default']; + } + else + { + $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') '; + $sql_default .= $return_array['default']; + } + } + + if (isset($column_data[2]) && $column_data[2] == 'auto_increment') + { + // $sql .= 'IDENTITY (1, 1) '; + $sql_default .= 'IDENTITY (1, 1) '; + } + + $return_array['textimage'] = $column_type === '[text]'; + + if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment')) + { + $sql .= 'NOT NULL'; + $sql_default .= 'NOT NULL'; + } + else + { + $sql .= 'NULL'; + $sql_default .= 'NULL'; + } + + $return_array['column_type_sql_default'] = $sql_default; + + $return_array['column_type_sql'] = $sql; + + return $return_array; + } + + /** + * {@inheritDoc} + */ + function sql_column_add($table_name, $column_name, $column_data, $inline = false) + { + $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); + $statements = array(); + + // Does not support AFTER, only through temporary table + $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default']; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_column_remove($table_name, $column_name, $inline = false) + { + $statements = array(); + + // We need the data here + $old_return_statements = $this->return_statements; + $this->return_statements = true; + + $indexes = $this->get_existing_indexes($table_name, $column_name); + $indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true)); + + // Drop any indexes + $recreate_indexes = array(); + if (!empty($indexes)) + { + foreach ($indexes as $index_name => $index_data) + { + $result = $this->sql_index_drop($table_name, $index_name); + $statements = array_merge($statements, $result); + if (sizeof($index_data) > 1) + { + // Remove this column from the index and recreate it + $recreate_indexes[$index_name] = array_diff($index_data, array($column_name)); + } + } + } + + // Drop default value constraint + $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); + $statements = array_merge($statements, $result); + + // Remove the column + $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']'; + + if (!empty($recreate_indexes)) + { + // Recreate indexes after we removed the column + foreach ($recreate_indexes as $index_name => $index_data) + { + $result = $this->sql_create_index($table_name, $index_name, $index_data); + $statements = array_merge($statements, $result); + } + } + + $this->return_statements = $old_return_statements; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_index_drop($table_name, $index_name) + { + $statements = array(); + + $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_table_drop($table_name) + { + $statements = array(); + + if (!$this->sql_table_exists($table_name)) + { + return $this->_sql_run_sql($statements); + } + + // the most basic operation, get rid of the table + $statements[] = 'DROP TABLE ' . $table_name; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_create_primary_key($table_name, $column, $inline = false) + { + $statements = array(); + + $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD "; + $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED ("; + $sql .= '[' . implode("],\n\t\t[", $column) . ']'; + $sql .= ')'; + + $statements[] = $sql; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_create_unique_index($table_name, $index_name, $column) + { + $statements = array(); + + $this->check_index_name_length($table_name, $index_name); + + $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_create_index($table_name, $index_name, $column) + { + $statements = array(); + + $this->check_index_name_length($table_name, $index_name); + + // remove index length + $column = preg_replace('#:.*$#', '', $column); + + $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; + + return $this->_sql_run_sql($statements); + } + + /** + * {@inheritDoc} + */ + function sql_list_index($table_name) + { + $index_array = array(); + $sql = "EXEC sp_statistics '$table_name'"; + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + if ($row['TYPE'] == 3) + { + $index_array[] = strtolower($row['INDEX_NAME']); + } + } + $this->db->sql_freeresult($result); + + return $index_array; + } + + /** + * {@inheritDoc} + */ + function sql_column_change($table_name, $column_name, $column_data, $inline = false) + { + $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); + $statements = array(); + + // We need the data here + $old_return_statements = $this->return_statements; + $this->return_statements = true; + + $indexes = $this->get_existing_indexes($table_name, $column_name); + $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true); + + // Drop any indexes + if (!empty($indexes) || !empty($unique_indexes)) + { + $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes)); + foreach ($drop_indexes as $index_name) + { + $result = $this->sql_index_drop($table_name, $index_name); + $statements = array_merge($statements, $result); + } + } + + // Drop default value constraint + $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); + $statements = array_merge($statements, $result); + + // Change the column + $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; + + if (!empty($column_data['default'])) + { + // Add new default value constraint + $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $this->db->sql_escape($column_data['default']) . ' FOR [' . $column_name . ']'; + } + + if (!empty($indexes)) + { + // Recreate indexes after we changed the column + foreach ($indexes as $index_name => $index_data) + { + $result = $this->sql_create_index($table_name, $index_name, $index_data); + $statements = array_merge($statements, $result); + } + } + + if (!empty($unique_indexes)) + { + // Recreate unique indexes after we changed the column + foreach ($unique_indexes as $index_name => $index_data) + { + $result = $this->sql_create_unique_index($table_name, $index_name, $index_data); + $statements = array_merge($statements, $result); + } + } + + $this->return_statements = $old_return_statements; + + return $this->_sql_run_sql($statements); + } + + /** + * Get queries to drop the default constraints of a column + * + * We need to drop the default constraints of a column, + * before being able to change their type or deleting them. + * + * @param string $table_name + * @param string $column_name + * @return array Array with SQL statements + */ + protected function mssql_get_drop_default_constraints_queries($table_name, $column_name) + { + $statements = array(); + if ($this->mssql_is_sql_server_2000()) + { + // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx + // Deprecated in SQL Server 2005 + $sql = "SELECT so.name AS def_name + FROM sysobjects so + JOIN sysconstraints sc ON so.id = sc.constid + WHERE object_name(so.parent_obj) = '{$table_name}' + AND so.xtype = 'D' + AND sc.colid = (SELECT colid FROM syscolumns + WHERE id = object_id('{$table_name}') + AND name = '{$column_name}')"; + } + else + { + $sql = "SELECT dobj.name AS def_name + FROM sys.columns col + LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D') + WHERE col.object_id = object_id('{$table_name}') + AND col.name = '{$column_name}' + AND dobj.name IS NOT NULL"; + } + + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']'; + } + $this->db->sql_freeresult($result); + + return $statements; + } + + /** + * Get a list with existing indexes for the column + * + * @param string $table_name + * @param string $column_name + * @param bool $unique Should we get unique indexes or normal ones + * @return array Array with Index name => columns + */ + public function get_existing_indexes($table_name, $column_name, $unique = false) + { + $existing_indexes = array(); + if ($this->mssql_is_sql_server_2000()) + { + // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx + // Deprecated in SQL Server 2005 + $sql = "SELECT DISTINCT ix.name AS phpbb_index_name + FROM sysindexes ix + INNER JOIN sysindexkeys ixc + ON ixc.id = ix.id + AND ixc.indid = ix.indid + INNER JOIN syscolumns cols + ON cols.colid = ixc.colid + AND cols.id = ix.id + WHERE ix.id = object_id('{$table_name}') + AND cols.name = '{$column_name}' + AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0'); + } + else + { + $sql = "SELECT DISTINCT ix.name AS phpbb_index_name + FROM sys.indexes ix + INNER JOIN sys.index_columns ixc + ON ixc.object_id = ix.object_id + AND ixc.index_id = ix.index_id + INNER JOIN sys.columns cols + ON cols.column_id = ixc.column_id + AND cols.object_id = ix.object_id + WHERE ix.object_id = object_id('{$table_name}') + AND cols.name = '{$column_name}' + AND ix.is_unique = " . ($unique ? '1' : '0'); + } + + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + if (!isset($row['is_unique']) || ($unique && $row['is_unique'] == 'UNIQUE') || (!$unique && $row['is_unique'] == 'NONUNIQUE')) + { + $existing_indexes[$row['phpbb_index_name']] = array(); + } + } + $this->db->sql_freeresult($result); + + if (empty($existing_indexes)) + { + return array(); + } + + if ($this->mssql_is_sql_server_2000()) + { + $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name + FROM sysindexes ix + INNER JOIN sysindexkeys ixc + ON ixc.id = ix.id + AND ixc.indid = ix.indid + INNER JOIN syscolumns cols + ON cols.colid = ixc.colid + AND cols.id = ix.id + WHERE ix.id = object_id('{$table_name}') + AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); + } + else + { + $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name + FROM sys.indexes ix + INNER JOIN sys.index_columns ixc + ON ixc.object_id = ix.object_id + AND ixc.index_id = ix.index_id + INNER JOIN sys.columns cols + ON cols.column_id = ixc.column_id + AND cols.object_id = ix.object_id + WHERE ix.object_id = object_id('{$table_name}') + AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); + } + + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + $existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name']; + } + $this->db->sql_freeresult($result); + + return $existing_indexes; + } + + /** + * Is the used MS SQL Server a SQL Server 2000? + * + * @return bool + */ + protected function mssql_is_sql_server_2000() + { + if ($this->is_sql_server_2000 === null) + { + $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version"; + $result = $this->db->sql_query($sql); + $properties = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8'; + } + + return $this->is_sql_server_2000; + } + +} diff --git a/phpBB/phpbb/db/tools/tools.php b/phpBB/phpbb/db/tools/tools.php index 152ec6b00f..d2bad21a3a 100644 --- a/phpBB/phpbb/db/tools/tools.php +++ b/phpBB/phpbb/db/tools/tools.php @@ -36,12 +36,6 @@ class tools implements tools_interface var $dbms_type_map = array(); /** - * Is the used MS SQL Server a SQL Server 2000? - * @var bool - */ - protected $is_sql_server_2000; - - /** * Get the column types for every database we support * * @return array @@ -109,66 +103,6 @@ class tools implements tools_interface 'VARBINARY' => 'varbinary(255)', ), - 'mssql' => array( - 'INT:' => '[int]', - 'BINT' => '[float]', - 'UINT' => '[int]', - 'UINT:' => '[int]', - 'TINT:' => '[int]', - 'USINT' => '[int]', - 'BOOL' => '[int]', - 'VCHAR' => '[varchar] (255)', - 'VCHAR:' => '[varchar] (%d)', - 'CHAR:' => '[char] (%d)', - 'XSTEXT' => '[varchar] (1000)', - 'STEXT' => '[varchar] (3000)', - 'TEXT' => '[varchar] (8000)', - 'MTEXT' => '[text]', - 'XSTEXT_UNI'=> '[varchar] (100)', - 'STEXT_UNI' => '[varchar] (255)', - 'TEXT_UNI' => '[varchar] (4000)', - 'MTEXT_UNI' => '[text]', - 'TIMESTAMP' => '[int]', - 'DECIMAL' => '[float]', - 'DECIMAL:' => '[float]', - 'PDECIMAL' => '[float]', - 'PDECIMAL:' => '[float]', - 'VCHAR_UNI' => '[varchar] (255)', - 'VCHAR_UNI:'=> '[varchar] (%d)', - 'VCHAR_CI' => '[varchar] (255)', - 'VARBINARY' => '[varchar] (255)', - ), - - 'mssqlnative' => array( - 'INT:' => '[int]', - 'BINT' => '[float]', - 'UINT' => '[int]', - 'UINT:' => '[int]', - 'TINT:' => '[int]', - 'USINT' => '[int]', - 'BOOL' => '[int]', - 'VCHAR' => '[varchar] (255)', - 'VCHAR:' => '[varchar] (%d)', - 'CHAR:' => '[char] (%d)', - 'XSTEXT' => '[varchar] (1000)', - 'STEXT' => '[varchar] (3000)', - 'TEXT' => '[varchar] (8000)', - 'MTEXT' => '[text]', - 'XSTEXT_UNI'=> '[varchar] (100)', - 'STEXT_UNI' => '[varchar] (255)', - 'TEXT_UNI' => '[varchar] (4000)', - 'MTEXT_UNI' => '[text]', - 'TIMESTAMP' => '[int]', - 'DECIMAL' => '[float]', - 'DECIMAL:' => '[float]', - 'PDECIMAL' => '[float]', - 'PDECIMAL:' => '[float]', - 'VCHAR_UNI' => '[varchar] (255)', - 'VCHAR_UNI:'=> '[varchar] (%d)', - 'VCHAR_CI' => '[varchar] (255)', - 'VARBINARY' => '[varchar] (255)', - ), - 'oracle' => array( 'INT:' => 'number(%d)', 'BINT' => 'number(20)', @@ -298,12 +232,6 @@ class tools implements tools_interface var $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP'); /** - * A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules. - * @var array - */ - var $supported_dbms = array('mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite', 'sqlite3'); - - /** * This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array). * This mode has no effect on some methods (inserting of data for example). This is expressed within the methods command. */ @@ -344,15 +272,6 @@ class tools implements tools_interface $this->sql_layer = 'mysql_41'; break; - case 'mssql': - case 'mssql_odbc': - $this->sql_layer = 'mssql'; - break; - - case 'mssqlnative': - $this->sql_layer = 'mssqlnative'; - break; - default: $this->sql_layer = $this->db->get_sql_layer(); break; @@ -396,14 +315,6 @@ class tools implements tools_interface AND name <> "sqlite_sequence"'; break; - case 'mssql': - case 'mssql_odbc': - case 'mssqlnative': - $sql = "SELECT name - FROM sysobjects - WHERE type='U'"; - break; - case 'postgres': $sql = 'SELECT relname FROM pg_stat_user_tables'; @@ -469,26 +380,7 @@ class tools implements tools_interface $create_sequence = false; // Begin table sql statement - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n"; - break; - - default: - $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n"; - break; - } - - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - if (!isset($table_data['PRIMARY_KEY'])) - { - $table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment'); - $table_data['PRIMARY_KEY'] = 'mssqlindex'; - } - } + $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n"; // Iterate through the columns to create a table foreach ($table_data['COLUMNS'] as $column_name => $column_data) @@ -502,17 +394,7 @@ class tools implements tools_interface } // here we add the definition of the new column to the list of columns - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default']; - break; - - default: - $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql']; - break; - } + $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql']; // see if we have found a primary key set due to a column definition if we have found it, we can stop looking if (!$primary_key_gen) @@ -530,16 +412,6 @@ class tools implements tools_interface // this makes up all the columns in the create table statement $table_sql .= implode(",\n", $columns); - // Close the table for two DBMS and add to the statements - switch ($this->sql_layer) - { - case 'mssql': - case 'mssqlnative': - $table_sql .= "\n);"; - $statements[] = $table_sql; - break; - } - // we have yet to create a primary key for this table, // this means that we can add the one we really wanted instead if (!$primary_key_gen) @@ -562,21 +434,6 @@ class tools implements tools_interface $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; break; - case 'mssql': - case 'mssqlnative': - // We need the data here - $old_return_statements = $this->return_statements; - $this->return_statements = true; - - $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']); - foreach ($primary_key_stmts as $pk_stmt) - { - $statements[] = $pk_stmt; - } - - $this->return_statements = $old_return_statements; - break; - case 'oracle': $table_sql .= ",\n\t CONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; break; @@ -1073,16 +930,6 @@ class tools implements tools_interface AND a.attrelid = c.oid"; break; - // same deal with PostgreSQL, we must perform more complex operations than - // we technically could - case 'mssql': - case 'mssqlnative': - $sql = "SELECT c.name - FROM syscolumns c - LEFT JOIN sysobjects o ON c.id = o.id - WHERE o.name = '{$table_name}'"; - break; - case 'oracle': $sql = "SELECT column_name FROM user_tab_columns @@ -1154,27 +1001,6 @@ class tools implements tools_interface */ function sql_index_exists($table_name, $index_name) { - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - $sql = "EXEC sp_statistics '$table_name'"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - if ($row['TYPE'] == 3) - { - if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - } - $this->db->sql_freeresult($result); - - return false; - } - switch ($this->sql_layer) { case 'postgres': @@ -1246,27 +1072,6 @@ class tools implements tools_interface */ function sql_unique_index_exists($table_name, $index_name) { - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - $sql = "EXEC sp_statistics '$table_name'"; - $result = $this->db->sql_query($sql); - - while ($row = $this->db->sql_fetchrow($result)) - { - // Usually NON_UNIQUE is the column we want to check, but we allow for both - if ($row['TYPE'] == 3) - { - if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) - { - $this->db->sql_freeresult($result); - return true; - } - } - } - $this->db->sql_freeresult($result); - return false; - } - switch ($this->sql_layer) { case 'postgres': @@ -1410,50 +1215,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - $sql .= " {$column_type} "; - $sql_default = " {$column_type} "; - - // For adding columns we need the default definition - if (!is_null($column_data[1])) - { - // For hexadecimal values do not use single quotes - if (strpos($column_data[1], '0x') === 0) - { - $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') '; - $sql_default .= $return_array['default']; - } - else - { - $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') '; - $sql_default .= $return_array['default']; - } - } - - if (isset($column_data[2]) && $column_data[2] == 'auto_increment') - { -// $sql .= 'IDENTITY (1, 1) '; - $sql_default .= 'IDENTITY (1, 1) '; - } - - $return_array['textimage'] = $column_type === '[text]'; - - if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment')) - { - $sql .= 'NOT NULL'; - $sql_default .= 'NOT NULL'; - } - else - { - $sql .= 'NULL'; - $sql_default .= 'NULL'; - } - - $return_array['column_type_sql_default'] = $sql_default; - - break; - case 'mysql_40': case 'mysql_41': $sql .= " {$column_type} "; @@ -1593,6 +1354,7 @@ class tools implements tools_interface */ function get_column_type($column_map_type) { + $column_type = ''; if (strpos($column_map_type, ':') !== false) { list($orig_column_type, $column_length) = explode(':', $column_map_type); @@ -1653,12 +1415,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - // Does not support AFTER, only through temporary table - $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default']; - break; - case 'mysql_40': case 'mysql_41': $after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : ''; @@ -1770,51 +1526,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - // We need the data here - $old_return_statements = $this->return_statements; - $this->return_statements = true; - - $indexes = $this->get_existing_indexes($table_name, $column_name); - $indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true)); - - // Drop any indexes - $recreate_indexes = array(); - if (!empty($indexes)) - { - foreach ($indexes as $index_name => $index_data) - { - $result = $this->sql_index_drop($table_name, $index_name); - $statements = array_merge($statements, $result); - if (sizeof($index_data) > 1) - { - // Remove this column from the index and recreate it - $recreate_indexes[$index_name] = array_diff($index_data, array($column_name)); - } - } - } - - // Drop default value constraint - $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); - $statements = array_merge($statements, $result); - - // Remove the column - $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']'; - - if (!empty($recreate_indexes)) - { - // Recreate indexes after we removed the column - foreach ($recreate_indexes as $index_name => $index_data) - { - $result = $this->sql_create_index($table_name, $index_name, $index_data); - $statements = array_merge($statements, $result); - } - } - - $this->return_statements = $old_return_statements; - break; - case 'mysql_40': case 'mysql_41': $statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`'; @@ -1899,11 +1610,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; - break; - case 'mysql_40': case 'mysql_41': $statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name; @@ -1988,16 +1694,6 @@ class tools implements tools_interface $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')'; break; - case 'mssql': - case 'mssqlnative': - $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD "; - $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED ("; - $sql .= '[' . implode("],\n\t\t[", $column) . ']'; - $sql .= ')'; - - $statements[] = $sql; - break; - case 'oracle': $statements[] = 'ALTER TABLE ' . $table_name . ' add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')'; break; @@ -2064,12 +1760,7 @@ class tools implements tools_interface { $statements = array(); - $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) - if (strlen($table_name . '_' . $index_name) - strlen($table_prefix) > 24) - { - $max_length = strlen($table_prefix) + 24; - trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); - } + $this->check_index_name_length($table_name, $index_name); switch ($this->sql_layer) { @@ -2084,11 +1775,6 @@ class tools implements tools_interface case 'mysql_41': $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; - break; } return $this->_sql_run_sql($statements); @@ -2101,12 +1787,7 @@ class tools implements tools_interface { $statements = array(); - $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) - if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) - { - $max_length = strlen($table_prefix) + 24; - trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); - } + $this->check_index_name_length($table_name, $index_name); // remove index length unless MySQL4 if ('mysql_40' != $this->sql_layer) @@ -2137,96 +1818,91 @@ class tools implements tools_interface case 'mysql_41': $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . ' (' . implode(', ', $column) . ')'; break; - - case 'mssql': - case 'mssqlnative': - $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; - break; } return $this->_sql_run_sql($statements); } /** + * Check whether the index name is too long + * + * @param string $table_name + * @param string $index_name + */ + protected function check_index_name_length($table_name, $index_name) + { + $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) + if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) + { + $max_length = strlen($table_prefix) + 24; + trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); + } + } + + /** * {@inheritDoc} */ function sql_list_index($table_name) { $index_array = array(); - if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') - { - $sql = "EXEC sp_statistics '$table_name'"; - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - if ($row['TYPE'] == 3) - { - $index_array[] = $row['INDEX_NAME']; - } - } - $this->db->sql_freeresult($result); - } - else + switch ($this->sql_layer) { - switch ($this->sql_layer) - { - case 'postgres': - $sql = "SELECT ic.relname as index_name - FROM pg_class bc, pg_class ic, pg_index i - WHERE (bc.oid = i.indrelid) - AND (ic.oid = i.indexrelid) - AND (bc.relname = '" . $table_name . "') - AND (i.indisunique != 't') - AND (i.indisprimary != 't')"; - $col = 'index_name'; + case 'postgres': + $sql = "SELECT ic.relname as index_name + FROM pg_class bc, pg_class ic, pg_index i + WHERE (bc.oid = i.indrelid) + AND (ic.oid = i.indexrelid) + AND (bc.relname = '" . $table_name . "') + AND (i.indisunique != 't') + AND (i.indisprimary != 't')"; + $col = 'index_name'; break; - case 'mysql_40': - case 'mysql_41': - $sql = 'SHOW KEYS - FROM ' . $table_name; - $col = 'Key_name'; + case 'mysql_40': + case 'mysql_41': + $sql = 'SHOW KEYS + FROM ' . $table_name; + $col = 'Key_name'; break; - case 'oracle': - $sql = "SELECT index_name - FROM user_indexes - WHERE table_name = '" . strtoupper($table_name) . "' - AND generated = 'N' - AND uniqueness = 'NONUNIQUE'"; - $col = 'index_name'; + case 'oracle': + $sql = "SELECT index_name + FROM user_indexes + WHERE table_name = '" . strtoupper($table_name) . "' + AND generated = 'N' + AND uniqueness = 'NONUNIQUE'"; + $col = 'index_name'; break; - case 'sqlite': - case 'sqlite3': - $sql = "PRAGMA index_info('" . $table_name . "');"; - $col = 'name'; + case 'sqlite': + case 'sqlite3': + $sql = "PRAGMA index_info('" . $table_name . "');"; + $col = 'name'; break; - } + } - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) - { - continue; - } + continue; + } - switch ($this->sql_layer) - { - case 'oracle': - case 'postgres': - case 'sqlite': - case 'sqlite3': - $row[$col] = substr($row[$col], strlen($table_name) + 1); + switch ($this->sql_layer) + { + case 'oracle': + case 'postgres': + case 'sqlite': + case 'sqlite3': + $row[$col] = substr($row[$col], strlen($table_name) + 1); break; - } - - $index_array[] = $row[$col]; } - $this->db->sql_freeresult($result); + + $index_array[] = $row[$col]; } + $this->db->sql_freeresult($result); return array_map('strtolower', $index_array); } @@ -2254,62 +1930,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - // We need the data here - $old_return_statements = $this->return_statements; - $this->return_statements = true; - - $indexes = $this->get_existing_indexes($table_name, $column_name); - $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true); - - // Drop any indexes - if (!empty($indexes) || !empty($unique_indexes)) - { - $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes)); - foreach ($drop_indexes as $index_name) - { - $result = $this->sql_index_drop($table_name, $index_name); - $statements = array_merge($statements, $result); - } - } - - // Drop default value constraint - $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); - $statements = array_merge($statements, $result); - - // Change the column - $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; - - if (!empty($column_data['default'])) - { - // Add new default value constraint - $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $this->db->sql_escape($column_data['default']) . ' FOR [' . $column_name . ']'; - } - - if (!empty($indexes)) - { - // Recreate indexes after we changed the column - foreach ($indexes as $index_name => $index_data) - { - $result = $this->sql_create_index($table_name, $index_name, $index_data); - $statements = array_merge($statements, $result); - } - } - - if (!empty($unique_indexes)) - { - // Recreate unique indexes after we changed the column - foreach ($unique_indexes as $index_name => $index_data) - { - $result = $this->sql_create_unique_index($table_name, $index_name, $index_data); - $statements = array_merge($statements, $result); - } - } - - $this->return_statements = $old_return_statements; - break; - case 'mysql_40': case 'mysql_41': $statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql']; @@ -2512,52 +2132,6 @@ class tools implements tools_interface } /** - * Get queries to drop the default constraints of a column - * - * We need to drop the default constraints of a column, - * before being able to change their type or deleting them. - * - * @param string $table_name - * @param string $column_name - * @return array Array with SQL statements - */ - protected function mssql_get_drop_default_constraints_queries($table_name, $column_name) - { - $statements = array(); - if ($this->mssql_is_sql_server_2000()) - { - // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx - // Deprecated in SQL Server 2005 - $sql = "SELECT so.name AS def_name - FROM sysobjects so - JOIN sysconstraints sc ON so.id = sc.constid - WHERE object_name(so.parent_obj) = '{$table_name}' - AND so.xtype = 'D' - AND sc.colid = (SELECT colid FROM syscolumns - WHERE id = object_id('{$table_name}') - AND name = '{$column_name}')"; - } - else - { - $sql = "SELECT dobj.name AS def_name - FROM sys.columns col - LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D') - WHERE col.object_id = object_id('{$table_name}') - AND col.name = '{$column_name}' - AND dobj.name IS NOT NULL"; - } - - $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) - { - $statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']'; - } - $this->db->sql_freeresult($result); - - return $statements; - } - - /** * Get a list with existing indexes for the column * * @param string $table_name @@ -2584,40 +2158,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - if ($this->mssql_is_sql_server_2000()) - { - // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx - // Deprecated in SQL Server 2005 - $sql = "SELECT DISTINCT ix.name AS phpbb_index_name - FROM sysindexes ix - INNER JOIN sysindexkeys ixc - ON ixc.id = ix.id - AND ixc.indid = ix.indid - INNER JOIN syscolumns cols - ON cols.colid = ixc.colid - AND cols.id = ix.id - WHERE ix.id = object_id('{$table_name}') - AND cols.name = '{$column_name}' - AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0'); - } - else - { - $sql = "SELECT DISTINCT ix.name AS phpbb_index_name - FROM sys.indexes ix - INNER JOIN sys.index_columns ixc - ON ixc.object_id = ix.object_id - AND ixc.index_id = ix.index_id - INNER JOIN sys.columns cols - ON cols.column_id = ixc.column_id - AND cols.object_id = ix.object_id - WHERE ix.object_id = object_id('{$table_name}') - AND cols.name = '{$column_name}' - AND ix.is_unique = " . ($unique ? '1' : '0'); - } - break; - case 'oracle': $sql = "SELECT ix.index_name AS phpbb_index_name, ix.uniqueness AS is_unique FROM all_ind_columns ixc, all_indexes ix @@ -2644,36 +2184,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mssql': - case 'mssqlnative': - if ($this->mssql_is_sql_server_2000()) - { - $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name - FROM sysindexes ix - INNER JOIN sysindexkeys ixc - ON ixc.id = ix.id - AND ixc.indid = ix.indid - INNER JOIN syscolumns cols - ON cols.colid = ixc.colid - AND cols.id = ix.id - WHERE ix.id = object_id('{$table_name}') - AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); - } - else - { - $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name - FROM sys.indexes ix - INNER JOIN sys.index_columns ixc - ON ixc.object_id = ix.object_id - AND ixc.index_id = ix.index_id - INNER JOIN sys.columns cols - ON cols.column_id = ixc.column_id - AND cols.object_id = ix.object_id - WHERE ix.object_id = object_id('{$table_name}') - AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); - } - break; - case 'oracle': $sql = "SELECT index_name AS phpbb_index_name, column_name AS phpbb_column_name FROM all_ind_columns @@ -2693,25 +2203,6 @@ class tools implements tools_interface } /** - * Is the used MS SQL Server a SQL Server 2000? - * - * @return bool - */ - protected function mssql_is_sql_server_2000() - { - if ($this->is_sql_server_2000 === null) - { - $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version"; - $result = $this->db->sql_query($sql); - $properties = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8'; - } - - return $this->is_sql_server_2000; - } - - /** * Returns the Queries which are required to recreate a table including indexes * * @param string $table_name diff --git a/phpBB/phpbb/search/fulltext_sphinx.php b/phpBB/phpbb/search/fulltext_sphinx.php index 0be646ff06..a5ad96b114 100644 --- a/phpBB/phpbb/search/fulltext_sphinx.php +++ b/phpBB/phpbb/search/fulltext_sphinx.php @@ -136,7 +136,8 @@ class fulltext_sphinx $this->auth = $auth; // Initialize \phpbb\db\tools\tools object - $this->db_tools = new \phpbb\db\tools\tools($this->db); + global $phpbb_container; // TODO inject into object + $this->db_tools = $phpbb_container->get('dbal.tools'); if(!$this->config['fulltext_sphinx_id']) { diff --git a/tests/dbal/auto_increment_test.php b/tests/dbal/auto_increment_test.php index 057bcb5bac..39eb6835ff 100644 --- a/tests/dbal/auto_increment_test.php +++ b/tests/dbal/auto_increment_test.php @@ -30,7 +30,8 @@ class phpbb_dbal_auto_increment_test extends phpbb_database_test_case parent::setUp(); $this->db = $this->new_dbal(); - $this->tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $this->tools = $factory->get($this->db); $this->table_data = array( 'COLUMNS' => array( diff --git a/tests/dbal/db_tools_test.php b/tests/dbal/db_tools_test.php index c09b34f55c..aa0b6ccf48 100644 --- a/tests/dbal/db_tools_test.php +++ b/tests/dbal/db_tools_test.php @@ -32,7 +32,8 @@ class phpbb_dbal_db_tools_test extends phpbb_database_test_case parent::setUp(); $this->db = $this->new_dbal(); - $this->tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $this->tools = $factory->get($this->db); $this->table_data = array( 'COLUMNS' => array( diff --git a/tests/dbal/migrator_test.php b/tests/dbal/migrator_test.php index 9b8383fc88..6416a2fcf7 100644 --- a/tests/dbal/migrator_test.php +++ b/tests/dbal/migrator_test.php @@ -38,7 +38,8 @@ class phpbb_dbal_migrator_test extends phpbb_database_test_case parent::setUp(); $this->db = $this->new_dbal(); - $this->db_tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $this->db_tools = $factory->get($this->db); $this->config = new \phpbb\config\db($this->db, new phpbb_mock_cache, 'phpbb_config'); diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php index c737d33f95..0126216701 100644 --- a/tests/extension/manager_test.php +++ b/tests/extension/manager_test.php @@ -150,7 +150,8 @@ class phpbb_extension_manager_test extends phpbb_database_test_case $config = new \phpbb\config\config(array('version' => PHPBB_VERSION)); $db = $this->new_dbal(); - $db_tools = new \phpbb\db\tools\tools($db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db); $phpbb_root_path = __DIR__ . './../../phpBB/'; $php_ext = 'php'; $table_prefix = 'phpbb_'; diff --git a/tests/extension/metadata_manager_test.php b/tests/extension/metadata_manager_test.php index bd7acf12f9..8514ed3dbd 100644 --- a/tests/extension/metadata_manager_test.php +++ b/tests/extension/metadata_manager_test.php @@ -41,7 +41,8 @@ class phpbb_extension_metadata_manager_test extends phpbb_database_test_case 'version' => '3.1.0', )); $this->db = $this->new_dbal(); - $this->db_tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $this->db_tools = $factory->get($this->db); $this->phpbb_root_path = dirname(__FILE__) . '/'; $this->phpEx = 'php'; $this->user = new \phpbb\user('\phpbb\datetime'); diff --git a/tests/migrator/convert_timezones_test.php b/tests/migrator/convert_timezones_test.php index 80a43b4035..f8d780da0c 100644 --- a/tests/migrator/convert_timezones_test.php +++ b/tests/migrator/convert_timezones_test.php @@ -18,7 +18,8 @@ class phpbb_migrator_convert_timezones_test extends phpbb_database_test_case public function getDataSet() { $this->db = $this->new_dbal(); - $db_tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($this->db); // user_dst doesn't exist anymore, must re-add it to test this $db_tools->sql_column_add('phpbb_users', 'user_dst', array('BOOL', 1)); @@ -55,11 +56,12 @@ class phpbb_migrator_convert_timezones_test extends phpbb_database_test_case global $phpbb_root_path, $phpEx; $this->db = $this->new_dbal(); + $factory = new \phpbb\db\tools\factory(); $this->migration = new \phpbb\db\migration\data\v310\timezone( new \phpbb\config\config(array()), $this->db, - new \phpbb\db\tools\tools($this->db), + $factory->get($this->db), $phpbb_root_path, $phpEx, 'phpbb_' @@ -90,7 +92,8 @@ class phpbb_migrator_convert_timezones_test extends phpbb_database_test_case } $this->db->sql_freeresult($result); - $db_tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($this->db); // Remove the user_dst field again $db_tools->sql_column_remove('phpbb_users', 'user_dst'); diff --git a/tests/migrator/schema_generator_test.php b/tests/migrator/schema_generator_test.php index 1e3b489426..40a8343e22 100644 --- a/tests/migrator/schema_generator_test.php +++ b/tests/migrator/schema_generator_test.php @@ -30,7 +30,8 @@ class schema_generator_test extends phpbb_test_case $this->config = new \phpbb\config\config(array()); $this->db = new \phpbb\db\driver\sqlite(); - $this->db_tools = new \phpbb\db\tools\tools($this->db); + $factory = new \phpbb\db\tools\factory(); + $this->db_tools = $factory->get($this->db); $this->table_prefix = 'phpbb_'; } diff --git a/tests/notification/convert_test.php b/tests/notification/convert_test.php index ad6e5849d3..4a7fd89409 100644 --- a/tests/notification/convert_test.php +++ b/tests/notification/convert_test.php @@ -28,11 +28,12 @@ class phpbb_notification_convert_test extends phpbb_database_test_case global $phpbb_root_path, $phpEx; $this->db = $this->new_dbal(); + $factory = new \phpbb\db\tools\factory(); $this->migration = new \phpbb\db\migration\data\v310\notification_options_reconvert( new \phpbb\config\config(array()), $this->db, - new \phpbb\db\tools\tools($this->db), + $factory->get($this->db), $phpbb_root_path, $phpEx, 'phpbb_' diff --git a/tests/test_framework/phpbb_database_test_case.php b/tests/test_framework/phpbb_database_test_case.php index f630f4ab52..903158d3c6 100644 --- a/tests/test_framework/phpbb_database_test_case.php +++ b/tests/test_framework/phpbb_database_test_case.php @@ -77,7 +77,10 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test global $table_prefix; $db = new \phpbb\db\driver\sqlite(); - $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, new \phpbb\db\tools\tools($db, true), $phpbb_root_path, $phpEx, $table_prefix); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db, true); + + $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, $db_tools, $phpbb_root_path, $phpEx, $table_prefix); file_put_contents(self::$schema_file, json_encode($schema_generator->get_schema())); } diff --git a/tests/test_framework/phpbb_database_test_connection_manager.php b/tests/test_framework/phpbb_database_test_connection_manager.php index ba641c6fb7..4f38ccc0d8 100644 --- a/tests/test_framework/phpbb_database_test_connection_manager.php +++ b/tests/test_framework/phpbb_database_test_connection_manager.php @@ -370,11 +370,15 @@ class phpbb_database_test_connection_manager ->get_classes(); $db = new \phpbb\db\driver\sqlite(); - $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, new \phpbb\db\tools\tools($db, true), $phpbb_root_path, $phpEx, $table_prefix); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db, true); + + $schema_generator = new \phpbb\db\migration\schema_generator($classes, new \phpbb\config\config(array()), $db, $db_tools, $phpbb_root_path, $phpEx, $table_prefix); $db_table_schema = $schema_generator->get_schema(); } - $db_tools = new \phpbb\db\tools\tools($db, true); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db, true); foreach ($db_table_schema as $table_name => $table_data) { $queries = $db_tools->sql_create_table( diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index 93876479d5..de18fb2cf2 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -230,7 +230,8 @@ class phpbb_functional_test_case extends phpbb_test_case $config = new \phpbb\config\config(array()); $db = $this->get_db(); - $db_tools = new \phpbb\db\tools\tools($db); + $factory = new \phpbb\db\tools\factory(); + $db_tools = $factory->get($db); $container = new phpbb_mock_container_builder(); $migrator = new \phpbb\db\migrator( |