[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/phpbb/db/ -> tools.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  namespace phpbb\db;
  15  
  16  /**
  17  * Database Tools for handling cross-db actions such as altering columns, etc.
  18  * Currently not supported is returning SQL for creating tables.
  19  */
  20  class tools
  21  {
  22      /**
  23      * Current sql layer
  24      */
  25      var $sql_layer = '';
  26  
  27      /**
  28      * @var object DB object
  29      */
  30      var $db = null;
  31  
  32      /**
  33      * The Column types for every database we support
  34      * @var array
  35      */
  36      var $dbms_type_map = array();
  37  
  38      /**
  39      * Is the used MS SQL Server a SQL Server 2000?
  40      * @var bool
  41      */
  42      protected $is_sql_server_2000;
  43  
  44      /**
  45      * Get the column types for every database we support
  46      *
  47      * @return array
  48      */
  49  	public static function get_dbms_type_map()
  50      {
  51          return array(
  52              'mysql_41'    => array(
  53                  'INT:'        => 'int(%d)',
  54                  'BINT'        => 'bigint(20)',
  55                  'UINT'        => 'mediumint(8) UNSIGNED',
  56                  'UINT:'        => 'int(%d) UNSIGNED',
  57                  'TINT:'        => 'tinyint(%d)',
  58                  'USINT'        => 'smallint(4) UNSIGNED',
  59                  'BOOL'        => 'tinyint(1) UNSIGNED',
  60                  'VCHAR'        => 'varchar(255)',
  61                  'VCHAR:'    => 'varchar(%d)',
  62                  'CHAR:'        => 'char(%d)',
  63                  'XSTEXT'    => 'text',
  64                  'XSTEXT_UNI'=> 'varchar(100)',
  65                  'STEXT'        => 'text',
  66                  'STEXT_UNI'    => 'varchar(255)',
  67                  'TEXT'        => 'text',
  68                  'TEXT_UNI'    => 'text',
  69                  'MTEXT'        => 'mediumtext',
  70                  'MTEXT_UNI'    => 'mediumtext',
  71                  'TIMESTAMP'    => 'int(11) UNSIGNED',
  72                  'DECIMAL'    => 'decimal(5,2)',
  73                  'DECIMAL:'    => 'decimal(%d,2)',
  74                  'PDECIMAL'    => 'decimal(6,3)',
  75                  'PDECIMAL:'    => 'decimal(%d,3)',
  76                  'VCHAR_UNI'    => 'varchar(255)',
  77                  'VCHAR_UNI:'=> 'varchar(%d)',
  78                  'VCHAR_CI'    => 'varchar(255)',
  79                  'VARBINARY'    => 'varbinary(255)',
  80              ),
  81  
  82              'mysql_40'    => array(
  83                  'INT:'        => 'int(%d)',
  84                  'BINT'        => 'bigint(20)',
  85                  'UINT'        => 'mediumint(8) UNSIGNED',
  86                  'UINT:'        => 'int(%d) UNSIGNED',
  87                  'TINT:'        => 'tinyint(%d)',
  88                  'USINT'        => 'smallint(4) UNSIGNED',
  89                  'BOOL'        => 'tinyint(1) UNSIGNED',
  90                  'VCHAR'        => 'varbinary(255)',
  91                  'VCHAR:'    => 'varbinary(%d)',
  92                  'CHAR:'        => 'binary(%d)',
  93                  'XSTEXT'    => 'blob',
  94                  'XSTEXT_UNI'=> 'blob',
  95                  'STEXT'        => 'blob',
  96                  'STEXT_UNI'    => 'blob',
  97                  'TEXT'        => 'blob',
  98                  'TEXT_UNI'    => 'blob',
  99                  'MTEXT'        => 'mediumblob',
 100                  'MTEXT_UNI'    => 'mediumblob',
 101                  'TIMESTAMP'    => 'int(11) UNSIGNED',
 102                  'DECIMAL'    => 'decimal(5,2)',
 103                  'DECIMAL:'    => 'decimal(%d,2)',
 104                  'PDECIMAL'    => 'decimal(6,3)',
 105                  'PDECIMAL:'    => 'decimal(%d,3)',
 106                  'VCHAR_UNI'    => 'blob',
 107                  'VCHAR_UNI:'=> array('varbinary(%d)', 'limit' => array('mult', 3, 255, 'blob')),
 108                  'VCHAR_CI'    => 'blob',
 109                  'VARBINARY'    => 'varbinary(255)',
 110              ),
 111  
 112              'mssql'        => array(
 113                  'INT:'        => '[int]',
 114                  'BINT'        => '[float]',
 115                  'UINT'        => '[int]',
 116                  'UINT:'        => '[int]',
 117                  'TINT:'        => '[int]',
 118                  'USINT'        => '[int]',
 119                  'BOOL'        => '[int]',
 120                  'VCHAR'        => '[varchar] (255)',
 121                  'VCHAR:'    => '[varchar] (%d)',
 122                  'CHAR:'        => '[char] (%d)',
 123                  'XSTEXT'    => '[varchar] (1000)',
 124                  'STEXT'        => '[varchar] (3000)',
 125                  'TEXT'        => '[varchar] (8000)',
 126                  'MTEXT'        => '[text]',
 127                  'XSTEXT_UNI'=> '[varchar] (100)',
 128                  'STEXT_UNI'    => '[varchar] (255)',
 129                  'TEXT_UNI'    => '[varchar] (4000)',
 130                  'MTEXT_UNI'    => '[text]',
 131                  'TIMESTAMP'    => '[int]',
 132                  'DECIMAL'    => '[float]',
 133                  'DECIMAL:'    => '[float]',
 134                  'PDECIMAL'    => '[float]',
 135                  'PDECIMAL:'    => '[float]',
 136                  'VCHAR_UNI'    => '[varchar] (255)',
 137                  'VCHAR_UNI:'=> '[varchar] (%d)',
 138                  'VCHAR_CI'    => '[varchar] (255)',
 139                  'VARBINARY'    => '[varchar] (255)',
 140              ),
 141  
 142              'mssqlnative'    => array(
 143                  'INT:'        => '[int]',
 144                  'BINT'        => '[float]',
 145                  'UINT'        => '[int]',
 146                  'UINT:'        => '[int]',
 147                  'TINT:'        => '[int]',
 148                  'USINT'        => '[int]',
 149                  'BOOL'        => '[int]',
 150                  'VCHAR'        => '[varchar] (255)',
 151                  'VCHAR:'    => '[varchar] (%d)',
 152                  'CHAR:'        => '[char] (%d)',
 153                  'XSTEXT'    => '[varchar] (1000)',
 154                  'STEXT'        => '[varchar] (3000)',
 155                  'TEXT'        => '[varchar] (8000)',
 156                  'MTEXT'        => '[text]',
 157                  'XSTEXT_UNI'=> '[varchar] (100)',
 158                  'STEXT_UNI'    => '[varchar] (255)',
 159                  'TEXT_UNI'    => '[varchar] (4000)',
 160                  'MTEXT_UNI'    => '[text]',
 161                  'TIMESTAMP'    => '[int]',
 162                  'DECIMAL'    => '[float]',
 163                  'DECIMAL:'    => '[float]',
 164                  'PDECIMAL'    => '[float]',
 165                  'PDECIMAL:'    => '[float]',
 166                  'VCHAR_UNI'    => '[varchar] (255)',
 167                  'VCHAR_UNI:'=> '[varchar] (%d)',
 168                  'VCHAR_CI'    => '[varchar] (255)',
 169                  'VARBINARY'    => '[varchar] (255)',
 170              ),
 171  
 172              'oracle'    => array(
 173                  'INT:'        => 'number(%d)',
 174                  'BINT'        => 'number(20)',
 175                  'UINT'        => 'number(8)',
 176                  'UINT:'        => 'number(%d)',
 177                  'TINT:'        => 'number(%d)',
 178                  'USINT'        => 'number(4)',
 179                  'BOOL'        => 'number(1)',
 180                  'VCHAR'        => 'varchar2(255)',
 181                  'VCHAR:'    => 'varchar2(%d)',
 182                  'CHAR:'        => 'char(%d)',
 183                  'XSTEXT'    => 'varchar2(1000)',
 184                  'STEXT'        => 'varchar2(3000)',
 185                  'TEXT'        => 'clob',
 186                  'MTEXT'        => 'clob',
 187                  'XSTEXT_UNI'=> 'varchar2(300)',
 188                  'STEXT_UNI'    => 'varchar2(765)',
 189                  'TEXT_UNI'    => 'clob',
 190                  'MTEXT_UNI'    => 'clob',
 191                  'TIMESTAMP'    => 'number(11)',
 192                  'DECIMAL'    => 'number(5, 2)',
 193                  'DECIMAL:'    => 'number(%d, 2)',
 194                  'PDECIMAL'    => 'number(6, 3)',
 195                  'PDECIMAL:'    => 'number(%d, 3)',
 196                  'VCHAR_UNI'    => 'varchar2(765)',
 197                  'VCHAR_UNI:'=> array('varchar2(%d)', 'limit' => array('mult', 3, 765, 'clob')),
 198                  'VCHAR_CI'    => 'varchar2(255)',
 199                  'VARBINARY'    => 'raw(255)',
 200              ),
 201  
 202              'sqlite'    => array(
 203                  'INT:'        => 'int(%d)',
 204                  'BINT'        => 'bigint(20)',
 205                  'UINT'        => 'INTEGER UNSIGNED', //'mediumint(8) UNSIGNED',
 206                  'UINT:'        => 'INTEGER UNSIGNED', // 'int(%d) UNSIGNED',
 207                  'TINT:'        => 'tinyint(%d)',
 208                  'USINT'        => 'INTEGER UNSIGNED', //'mediumint(4) UNSIGNED',
 209                  'BOOL'        => 'INTEGER UNSIGNED', //'tinyint(1) UNSIGNED',
 210                  'VCHAR'        => 'varchar(255)',
 211                  'VCHAR:'    => 'varchar(%d)',
 212                  'CHAR:'        => 'char(%d)',
 213                  'XSTEXT'    => 'text(65535)',
 214                  'STEXT'        => 'text(65535)',
 215                  'TEXT'        => 'text(65535)',
 216                  'MTEXT'        => 'mediumtext(16777215)',
 217                  'XSTEXT_UNI'=> 'text(65535)',
 218                  'STEXT_UNI'    => 'text(65535)',
 219                  'TEXT_UNI'    => 'text(65535)',
 220                  'MTEXT_UNI'    => 'mediumtext(16777215)',
 221                  'TIMESTAMP'    => 'INTEGER UNSIGNED', //'int(11) UNSIGNED',
 222                  'DECIMAL'    => 'decimal(5,2)',
 223                  'DECIMAL:'    => 'decimal(%d,2)',
 224                  'PDECIMAL'    => 'decimal(6,3)',
 225                  'PDECIMAL:'    => 'decimal(%d,3)',
 226                  'VCHAR_UNI'    => 'varchar(255)',
 227                  'VCHAR_UNI:'=> 'varchar(%d)',
 228                  'VCHAR_CI'    => 'varchar(255)',
 229                  'VARBINARY'    => 'blob',
 230              ),
 231  
 232              'sqlite3'    => array(
 233                  'INT:'        => 'INT(%d)',
 234                  'BINT'        => 'BIGINT(20)',
 235                  'UINT'        => 'INTEGER UNSIGNED',
 236                  'UINT:'        => 'INTEGER UNSIGNED',
 237                  'TINT:'        => 'TINYINT(%d)',
 238                  'USINT'        => 'INTEGER UNSIGNED',
 239                  'BOOL'        => 'INTEGER UNSIGNED',
 240                  'VCHAR'        => 'VARCHAR(255)',
 241                  'VCHAR:'    => 'VARCHAR(%d)',
 242                  'CHAR:'        => 'CHAR(%d)',
 243                  'XSTEXT'    => 'TEXT(65535)',
 244                  'STEXT'        => 'TEXT(65535)',
 245                  'TEXT'        => 'TEXT(65535)',
 246                  'MTEXT'        => 'MEDIUMTEXT(16777215)',
 247                  'XSTEXT_UNI'=> 'TEXT(65535)',
 248                  'STEXT_UNI'    => 'TEXT(65535)',
 249                  'TEXT_UNI'    => 'TEXT(65535)',
 250                  'MTEXT_UNI'    => 'MEDIUMTEXT(16777215)',
 251                  'TIMESTAMP'    => 'INTEGER UNSIGNED', //'int(11) UNSIGNED',
 252                  'DECIMAL'    => 'DECIMAL(5,2)',
 253                  'DECIMAL:'    => 'DECIMAL(%d,2)',
 254                  'PDECIMAL'    => 'DECIMAL(6,3)',
 255                  'PDECIMAL:'    => 'DECIMAL(%d,3)',
 256                  'VCHAR_UNI'    => 'VARCHAR(255)',
 257                  'VCHAR_UNI:'=> 'VARCHAR(%d)',
 258                  'VCHAR_CI'    => 'VARCHAR(255)',
 259                  'VARBINARY'    => 'BLOB',
 260              ),
 261  
 262              'postgres'    => array(
 263                  'INT:'        => 'INT4',
 264                  'BINT'        => 'INT8',
 265                  'UINT'        => 'INT4', // unsigned
 266                  'UINT:'        => 'INT4', // unsigned
 267                  'USINT'        => 'INT2', // unsigned
 268                  'BOOL'        => 'INT2', // unsigned
 269                  'TINT:'        => 'INT2',
 270                  'VCHAR'        => 'varchar(255)',
 271                  'VCHAR:'    => 'varchar(%d)',
 272                  'CHAR:'        => 'char(%d)',
 273                  'XSTEXT'    => 'varchar(1000)',
 274                  'STEXT'        => 'varchar(3000)',
 275                  'TEXT'        => 'varchar(8000)',
 276                  'MTEXT'        => 'TEXT',
 277                  'XSTEXT_UNI'=> 'varchar(100)',
 278                  'STEXT_UNI'    => 'varchar(255)',
 279                  'TEXT_UNI'    => 'varchar(4000)',
 280                  'MTEXT_UNI'    => 'TEXT',
 281                  'TIMESTAMP'    => 'INT4', // unsigned
 282                  'DECIMAL'    => 'decimal(5,2)',
 283                  'DECIMAL:'    => 'decimal(%d,2)',
 284                  'PDECIMAL'    => 'decimal(6,3)',
 285                  'PDECIMAL:'    => 'decimal(%d,3)',
 286                  'VCHAR_UNI'    => 'varchar(255)',
 287                  'VCHAR_UNI:'=> 'varchar(%d)',
 288                  'VCHAR_CI'    => 'varchar_ci',
 289                  'VARBINARY'    => 'bytea',
 290              ),
 291          );
 292      }
 293  
 294      /**
 295      * A list of types being unsigned for better reference in some db's
 296      * @var array
 297      */
 298      var $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP');
 299  
 300      /**
 301      * A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules.
 302      * @var array
 303      */
 304      var $supported_dbms = array('mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite', 'sqlite3');
 305  
 306      /**
 307      * This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array).
 308      * This mode has no effect on some methods (inserting of data for example). This is expressed within the methods command.
 309      */
 310      var $return_statements = false;
 311  
 312      /**
 313      * Constructor. Set DB Object and set {@link $return_statements return_statements}.
 314      *
 315      * @param \phpbb\db\driver\driver_interface    $db                    Database connection
 316      * @param bool        $return_statements    True if only statements should be returned and no SQL being executed
 317      */
 318  	public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
 319      {
 320          $this->db = $db;
 321          $this->return_statements = $return_statements;
 322  
 323          $this->dbms_type_map = self::get_dbms_type_map();
 324  
 325          // Determine mapping database type
 326          switch ($this->db->get_sql_layer())
 327          {
 328              case 'mysql':
 329                  $this->sql_layer = 'mysql_40';
 330              break;
 331  
 332              case 'mysql4':
 333                  if (version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
 334                  {
 335                      $this->sql_layer = 'mysql_41';
 336                  }
 337                  else
 338                  {
 339                      $this->sql_layer = 'mysql_40';
 340                  }
 341              break;
 342  
 343              case 'mysqli':
 344                  $this->sql_layer = 'mysql_41';
 345              break;
 346  
 347              case 'mssql':
 348              case 'mssql_odbc':
 349                  $this->sql_layer = 'mssql';
 350              break;
 351  
 352              case 'mssqlnative':
 353                  $this->sql_layer = 'mssqlnative';
 354              break;
 355  
 356              default:
 357                  $this->sql_layer = $this->db->get_sql_layer();
 358              break;
 359          }
 360      }
 361  
 362      /**
 363      * Setter for {@link $return_statements return_statements}.
 364      *
 365      * @param bool $return_statements True if SQL should not be executed but returned as strings
 366      * @return null
 367      */
 368  	public function set_return_statements($return_statements)
 369      {
 370          $this->return_statements = $return_statements;
 371      }
 372  
 373      /**
 374      * Gets a list of tables in the database.
 375      *
 376      * @return array        Array of table names  (all lower case)
 377      */
 378  	function sql_list_tables()
 379      {
 380          switch ($this->db->get_sql_layer())
 381          {
 382              case 'mysql':
 383              case 'mysql4':
 384              case 'mysqli':
 385                  $sql = 'SHOW TABLES';
 386              break;
 387  
 388              case 'sqlite':
 389                  $sql = 'SELECT name
 390                      FROM sqlite_master
 391                      WHERE type = "table"';
 392              break;
 393  
 394              case 'sqlite3':
 395                  $sql = 'SELECT name
 396                      FROM sqlite_master
 397                      WHERE type = "table"
 398                          AND name <> "sqlite_sequence"';
 399              break;
 400  
 401              case 'mssql':
 402              case 'mssql_odbc':
 403              case 'mssqlnative':
 404                  $sql = "SELECT name
 405                      FROM sysobjects
 406                      WHERE type='U'";
 407              break;
 408  
 409              case 'postgres':
 410                  $sql = 'SELECT relname
 411                      FROM pg_stat_user_tables';
 412              break;
 413  
 414              case 'oracle':
 415                  $sql = 'SELECT table_name
 416                      FROM USER_TABLES';
 417              break;
 418          }
 419  
 420          $result = $this->db->sql_query($sql);
 421  
 422          $tables = array();
 423          while ($row = $this->db->sql_fetchrow($result))
 424          {
 425              $name = current($row);
 426              $tables[$name] = $name;
 427          }
 428          $this->db->sql_freeresult($result);
 429  
 430          return $tables;
 431      }
 432  
 433      /**
 434      * Check if table exists
 435      *
 436      *
 437      * @param string    $table_name    The table name to check for
 438      * @return bool true if table exists, else false
 439      */
 440  	function sql_table_exists($table_name)
 441      {
 442          $this->db->sql_return_on_error(true);
 443          $result = $this->db->sql_query_limit('SELECT * FROM ' . $table_name, 1);
 444          $this->db->sql_return_on_error(false);
 445  
 446          if ($result)
 447          {
 448              $this->db->sql_freeresult($result);
 449              return true;
 450          }
 451  
 452          return false;
 453      }
 454  
 455      /**
 456      * Create SQL Table
 457      *
 458      * @param string    $table_name    The table name to create
 459      * @param array    $table_data    Array containing table data.
 460      * @return array    Statements if $return_statements is true.
 461      */
 462  	function sql_create_table($table_name, $table_data)
 463      {
 464          // holds the DDL for a column
 465          $columns = $statements = array();
 466  
 467          if ($this->sql_table_exists($table_name))
 468          {
 469              return $this->_sql_run_sql($statements);
 470          }
 471  
 472          // Begin transaction
 473          $statements[] = 'begin';
 474  
 475          // Determine if we have created a PRIMARY KEY in the earliest
 476          $primary_key_gen = false;
 477  
 478          // Determine if the table requires a sequence
 479          $create_sequence = false;
 480  
 481          // Begin table sql statement
 482          switch ($this->sql_layer)
 483          {
 484              case 'mssql':
 485              case 'mssqlnative':
 486                  $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
 487              break;
 488  
 489              default:
 490                  $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
 491              break;
 492          }
 493  
 494          if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
 495          {
 496              if (!isset($table_data['PRIMARY_KEY']))
 497              {
 498                  $table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment');
 499                  $table_data['PRIMARY_KEY'] = 'mssqlindex';
 500              }
 501          }
 502  
 503          // Iterate through the columns to create a table
 504          foreach ($table_data['COLUMNS'] as $column_name => $column_data)
 505          {
 506              // here lies an array, filled with information compiled on the column's data
 507              $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
 508  
 509              if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "$column_name}_gen"
 510              {
 511                  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);
 512              }
 513  
 514              // here we add the definition of the new column to the list of columns
 515              switch ($this->sql_layer)
 516              {
 517                  case 'mssql':
 518                  case 'mssqlnative':
 519                      $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default'];
 520                  break;
 521  
 522                  default:
 523                      $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
 524                  break;
 525              }
 526  
 527              // see if we have found a primary key set due to a column definition if we have found it, we can stop looking
 528              if (!$primary_key_gen)
 529              {
 530                  $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
 531              }
 532  
 533              // create sequence DDL based off of the existance of auto incrementing columns
 534              if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
 535              {
 536                  $create_sequence = $column_name;
 537              }
 538          }
 539  
 540          // this makes up all the columns in the create table statement
 541          $table_sql .= implode(",\n", $columns);
 542  
 543          // Close the table for two DBMS and add to the statements
 544          switch ($this->sql_layer)
 545          {
 546              case 'mssql':
 547              case 'mssqlnative':
 548                  $table_sql .= "\n);";
 549                  $statements[] = $table_sql;
 550              break;
 551          }
 552  
 553          // we have yet to create a primary key for this table,
 554          // this means that we can add the one we really wanted instead
 555          if (!$primary_key_gen)
 556          {
 557              // Write primary key
 558              if (isset($table_data['PRIMARY_KEY']))
 559              {
 560                  if (!is_array($table_data['PRIMARY_KEY']))
 561                  {
 562                      $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
 563                  }
 564  
 565                  switch ($this->sql_layer)
 566                  {
 567                      case 'mysql_40':
 568                      case 'mysql_41':
 569                      case 'postgres':
 570                      case 'sqlite':
 571                      case 'sqlite3':
 572                          $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
 573                      break;
 574  
 575                      case 'mssql':
 576                      case 'mssqlnative':
 577                          // We need the data here
 578                          $old_return_statements = $this->return_statements;
 579                          $this->return_statements = true;
 580  
 581                          $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']);
 582                          foreach ($primary_key_stmts as $pk_stmt)
 583                          {
 584                              $statements[] = $pk_stmt;
 585                          }
 586  
 587                          $this->return_statements = $old_return_statements;
 588                      break;
 589  
 590                      case 'oracle':
 591                          $table_sql .= ",\n\t CONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
 592                      break;
 593                  }
 594              }
 595          }
 596  
 597          // close the table
 598          switch ($this->sql_layer)
 599          {
 600              case 'mysql_41':
 601                  // make sure the table is in UTF-8 mode
 602                  $table_sql .= "\n) CHARACTER SET `utf8` COLLATE `utf8_bin`;";
 603                  $statements[] = $table_sql;
 604              break;
 605  
 606              case 'mysql_40':
 607              case 'sqlite':
 608              case 'sqlite3':
 609                  $table_sql .= "\n);";
 610                  $statements[] = $table_sql;
 611              break;
 612  
 613              case 'postgres':
 614                  // do we need to add a sequence for auto incrementing columns?
 615                  if ($create_sequence)
 616                  {
 617                      $statements[] = "CREATE SEQUENCE {$table_name}_seq;";
 618                  }
 619  
 620                  $table_sql .= "\n);";
 621                  $statements[] = $table_sql;
 622              break;
 623  
 624              case 'oracle':
 625                  $table_sql .= "\n)";
 626                  $statements[] = $table_sql;
 627  
 628                  // do we need to add a sequence and a tigger for auto incrementing columns?
 629                  if ($create_sequence)
 630                  {
 631                      // create the actual sequence
 632                      $statements[] = "CREATE SEQUENCE {$table_name}_seq";
 633  
 634                      // the trigger is the mechanism by which we increment the counter
 635                      $trigger = "CREATE OR REPLACE TRIGGER t_{$table_name}\n";
 636                      $trigger .= "BEFORE INSERT ON {$table_name}\n";
 637                      $trigger .= "FOR EACH ROW WHEN (\n";
 638                      $trigger .= "\tnew.{$create_sequence} IS NULL OR new.{$create_sequence} = 0\n";
 639                      $trigger .= ")\n";
 640                      $trigger .= "BEGIN\n";
 641                      $trigger .= "\tSELECT {$table_name}_seq.nextval\n";
 642                      $trigger .= "\tINTO :new.{$create_sequence}\n";
 643                      $trigger .= "\tFROM dual;\n";
 644                      $trigger .= "END;";
 645  
 646                      $statements[] = $trigger;
 647                  }
 648              break;
 649          }
 650  
 651          // Write Keys
 652          if (isset($table_data['KEYS']))
 653          {
 654              foreach ($table_data['KEYS'] as $key_name => $key_data)
 655              {
 656                  if (!is_array($key_data[1]))
 657                  {
 658                      $key_data[1] = array($key_data[1]);
 659                  }
 660  
 661                  $old_return_statements = $this->return_statements;
 662                  $this->return_statements = true;
 663  
 664                  $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]);
 665  
 666                  foreach ($key_stmts as $key_stmt)
 667                  {
 668                      $statements[] = $key_stmt;
 669                  }
 670  
 671                  $this->return_statements = $old_return_statements;
 672              }
 673          }
 674  
 675          // Commit Transaction
 676          $statements[] = 'commit';
 677  
 678          return $this->_sql_run_sql($statements);
 679      }
 680  
 681      /**
 682      * Handle passed database update array.
 683      * Expected structure...
 684      * Key being one of the following
 685      *    drop_tables: Drop tables
 686      *    add_tables: Add tables
 687      *    change_columns: Column changes (only type, not name)
 688      *    add_columns: Add columns to a table
 689      *    drop_keys: Dropping keys
 690      *    drop_columns: Removing/Dropping columns
 691      *    add_primary_keys: adding primary keys
 692      *    add_unique_index: adding an unique index
 693      *    add_index: adding an index (can be column:index_size if you need to provide size)
 694      *
 695      * The values are in this format:
 696      *        {TABLE NAME}        => array(
 697      *            {COLUMN NAME}        => array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}),
 698      *            {KEY/INDEX NAME}    => array({COLUMN NAMES}),
 699      *        )
 700      *
 701      * For more information have a look at /develop/create_schema_files.php (only available through SVN)
 702      */
 703  	function perform_schema_changes($schema_changes)
 704      {
 705          if (empty($schema_changes))
 706          {
 707              return;
 708          }
 709  
 710          $statements = array();
 711          $sqlite = false;
 712  
 713          // For SQLite we need to perform the schema changes in a much more different way
 714          if (($this->db->get_sql_layer() == 'sqlite' || $this->db->get_sql_layer() == 'sqlite3') && $this->return_statements)
 715          {
 716              $sqlite_data = array();
 717              $sqlite = true;
 718          }
 719  
 720          // Drop tables?
 721          if (!empty($schema_changes['drop_tables']))
 722          {
 723              foreach ($schema_changes['drop_tables'] as $table)
 724              {
 725                  // only drop table if it exists
 726                  if ($this->sql_table_exists($table))
 727                  {
 728                      $result = $this->sql_table_drop($table);
 729                      if ($this->return_statements)
 730                      {
 731                          $statements = array_merge($statements, $result);
 732                      }
 733                  }
 734              }
 735          }
 736  
 737          // Add tables?
 738          if (!empty($schema_changes['add_tables']))
 739          {
 740              foreach ($schema_changes['add_tables'] as $table => $table_data)
 741              {
 742                  $result = $this->sql_create_table($table, $table_data);
 743                  if ($this->return_statements)
 744                  {
 745                      $statements = array_merge($statements, $result);
 746                  }
 747              }
 748          }
 749  
 750          // Change columns?
 751          if (!empty($schema_changes['change_columns']))
 752          {
 753              foreach ($schema_changes['change_columns'] as $table => $columns)
 754              {
 755                  foreach ($columns as $column_name => $column_data)
 756                  {
 757                      // If the column exists we change it, else we add it ;)
 758                      if ($column_exists = $this->sql_column_exists($table, $column_name))
 759                      {
 760                          $result = $this->sql_column_change($table, $column_name, $column_data, true);
 761                      }
 762                      else
 763                      {
 764                          $result = $this->sql_column_add($table, $column_name, $column_data, true);
 765                      }
 766  
 767                      if ($sqlite)
 768                      {
 769                          if ($column_exists)
 770                          {
 771                              $sqlite_data[$table]['change_columns'][] = $result;
 772                          }
 773                          else
 774                          {
 775                              $sqlite_data[$table]['add_columns'][] = $result;
 776                          }
 777                      }
 778                      else if ($this->return_statements)
 779                      {
 780                          $statements = array_merge($statements, $result);
 781                      }
 782                  }
 783              }
 784          }
 785  
 786          // Add columns?
 787          if (!empty($schema_changes['add_columns']))
 788          {
 789              foreach ($schema_changes['add_columns'] as $table => $columns)
 790              {
 791                  foreach ($columns as $column_name => $column_data)
 792                  {
 793                      // Only add the column if it does not exist yet
 794                      if ($column_exists = $this->sql_column_exists($table, $column_name))
 795                      {
 796                          continue;
 797                          // This is commented out here because it can take tremendous time on updates
 798  //                        $result = $this->sql_column_change($table, $column_name, $column_data, true);
 799                      }
 800                      else
 801                      {
 802                          $result = $this->sql_column_add($table, $column_name, $column_data, true);
 803                      }
 804  
 805                      if ($sqlite)
 806                      {
 807                          if ($column_exists)
 808                          {
 809                              continue;
 810  //                            $sqlite_data[$table]['change_columns'][] = $result;
 811                          }
 812                          else
 813                          {
 814                              $sqlite_data[$table]['add_columns'][] = $result;
 815                          }
 816                      }
 817                      else if ($this->return_statements)
 818                      {
 819                          $statements = array_merge($statements, $result);
 820                      }
 821                  }
 822              }
 823          }
 824  
 825          // Remove keys?
 826          if (!empty($schema_changes['drop_keys']))
 827          {
 828              foreach ($schema_changes['drop_keys'] as $table => $indexes)
 829              {
 830                  foreach ($indexes as $index_name)
 831                  {
 832                      if (!$this->sql_index_exists($table, $index_name))
 833                      {
 834                          continue;
 835                      }
 836  
 837                      $result = $this->sql_index_drop($table, $index_name);
 838  
 839                      if ($this->return_statements)
 840                      {
 841                          $statements = array_merge($statements, $result);
 842                      }
 843                  }
 844              }
 845          }
 846  
 847          // Drop columns?
 848          if (!empty($schema_changes['drop_columns']))
 849          {
 850              foreach ($schema_changes['drop_columns'] as $table => $columns)
 851              {
 852                  foreach ($columns as $column)
 853                  {
 854                      // Only remove the column if it exists...
 855                      if ($this->sql_column_exists($table, $column))
 856                      {
 857                          $result = $this->sql_column_remove($table, $column, true);
 858  
 859                          if ($sqlite)
 860                          {
 861                              $sqlite_data[$table]['drop_columns'][] = $result;
 862                          }
 863                          else if ($this->return_statements)
 864                          {
 865                              $statements = array_merge($statements, $result);
 866                          }
 867                      }
 868                  }
 869              }
 870          }
 871  
 872          // Add primary keys?
 873          if (!empty($schema_changes['add_primary_keys']))
 874          {
 875              foreach ($schema_changes['add_primary_keys'] as $table => $columns)
 876              {
 877                  $result = $this->sql_create_primary_key($table, $columns, true);
 878  
 879                  if ($sqlite)
 880                  {
 881                      $sqlite_data[$table]['primary_key'] = $result;
 882                  }
 883                  else if ($this->return_statements)
 884                  {
 885                      $statements = array_merge($statements, $result);
 886                  }
 887              }
 888          }
 889  
 890          // Add unique indexes?
 891          if (!empty($schema_changes['add_unique_index']))
 892          {
 893              foreach ($schema_changes['add_unique_index'] as $table => $index_array)
 894              {
 895                  foreach ($index_array as $index_name => $column)
 896                  {
 897                      if ($this->sql_unique_index_exists($table, $index_name))
 898                      {
 899                          continue;
 900                      }
 901  
 902                      $result = $this->sql_create_unique_index($table, $index_name, $column);
 903  
 904                      if ($this->return_statements)
 905                      {
 906                          $statements = array_merge($statements, $result);
 907                      }
 908                  }
 909              }
 910          }
 911  
 912          // Add indexes?
 913          if (!empty($schema_changes['add_index']))
 914          {
 915              foreach ($schema_changes['add_index'] as $table => $index_array)
 916              {
 917                  foreach ($index_array as $index_name => $column)
 918                  {
 919                      if ($this->sql_index_exists($table, $index_name))
 920                      {
 921                          continue;
 922                      }
 923  
 924                      $result = $this->sql_create_index($table, $index_name, $column);
 925  
 926                      if ($this->return_statements)
 927                      {
 928                          $statements = array_merge($statements, $result);
 929                      }
 930                  }
 931              }
 932          }
 933  
 934          if ($sqlite)
 935          {
 936              foreach ($sqlite_data as $table_name => $sql_schema_changes)
 937              {
 938                  // Create temporary table with original data
 939                  $statements[] = 'begin';
 940  
 941                  $sql = "SELECT sql
 942                      FROM sqlite_master
 943                      WHERE type = 'table'
 944                          AND name = '{$table_name}'
 945                      ORDER BY type DESC, name;";
 946                  $result = $this->db->sql_query($sql);
 947  
 948                  if (!$result)
 949                  {
 950                      continue;
 951                  }
 952  
 953                  $row = $this->db->sql_fetchrow($result);
 954                  $this->db->sql_freeresult($result);
 955  
 956                  // Create a backup table and populate it, destroy the existing one
 957                  $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']);
 958                  $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
 959                  $statements[] = 'DROP TABLE ' . $table_name;
 960  
 961                  // Get the columns...
 962                  preg_match('#\((.*)\)#s', $row['sql'], $matches);
 963  
 964                  $plain_table_cols = trim($matches[1]);
 965                  $new_table_cols = preg_split('/,(?![\s\w]+\))/m', $plain_table_cols);
 966                  $column_list = array();
 967  
 968                  foreach ($new_table_cols as $declaration)
 969                  {
 970                      $entities = preg_split('#\s+#', trim($declaration));
 971                      if ($entities[0] == 'PRIMARY')
 972                      {
 973                          continue;
 974                      }
 975                      $column_list[] = $entities[0];
 976                  }
 977  
 978                  // note down the primary key notation because sqlite only supports adding it to the end for the new table
 979                  $primary_key = false;
 980                  $_new_cols = array();
 981  
 982                  foreach ($new_table_cols as $key => $declaration)
 983                  {
 984                      $entities = preg_split('#\s+#', trim($declaration));
 985                      if ($entities[0] == 'PRIMARY')
 986                      {
 987                          $primary_key = $declaration;
 988                          continue;
 989                      }
 990                      $_new_cols[] = $declaration;
 991                  }
 992  
 993                  $new_table_cols = $_new_cols;
 994  
 995                  // First of all... change columns
 996                  if (!empty($sql_schema_changes['change_columns']))
 997                  {
 998                      foreach ($sql_schema_changes['change_columns'] as $column_sql)
 999                      {
1000                          foreach ($new_table_cols as $key => $declaration)
1001                          {
1002                              $entities = preg_split('#\s+#', trim($declaration));
1003                              if (strpos($column_sql, $entities[0] . ' ') === 0)
1004                              {
1005                                  $new_table_cols[$key] = $column_sql;
1006                              }
1007                          }
1008                      }
1009                  }
1010  
1011                  if (!empty($sql_schema_changes['add_columns']))
1012                  {
1013                      foreach ($sql_schema_changes['add_columns'] as $column_sql)
1014                      {
1015                          $new_table_cols[] = $column_sql;
1016                      }
1017                  }
1018  
1019                  // Now drop them...
1020                  if (!empty($sql_schema_changes['drop_columns']))
1021                  {
1022                      foreach ($sql_schema_changes['drop_columns'] as $column_name)
1023                      {
1024                          // Remove from column list...
1025                          $new_column_list = array();
1026                          foreach ($column_list as $key => $value)
1027                          {
1028                              if ($value === $column_name)
1029                              {
1030                                  continue;
1031                              }
1032  
1033                              $new_column_list[] = $value;
1034                          }
1035  
1036                          $column_list = $new_column_list;
1037  
1038                          // Remove from table...
1039                          $_new_cols = array();
1040                          foreach ($new_table_cols as $key => $declaration)
1041                          {
1042                              $entities = preg_split('#\s+#', trim($declaration));
1043                              if (strpos($column_name . ' ', $entities[0] . ' ') === 0)
1044                              {
1045                                  continue;
1046                              }
1047                              $_new_cols[] = $declaration;
1048                          }
1049                          $new_table_cols = $_new_cols;
1050                      }
1051                  }
1052  
1053                  // Primary key...
1054                  if (!empty($sql_schema_changes['primary_key']))
1055                  {
1056                      $new_table_cols[] = 'PRIMARY KEY (' . implode(', ', $sql_schema_changes['primary_key']) . ')';
1057                  }
1058                  // Add a new one or the old primary key
1059                  else if ($primary_key !== false)
1060                  {
1061                      $new_table_cols[] = $primary_key;
1062                  }
1063  
1064                  $columns = implode(',', $column_list);
1065  
1066                  // create a new table and fill it up. destroy the temp one
1067                  $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $new_table_cols) . ');';
1068                  $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
1069                  $statements[] = 'DROP TABLE ' . $table_name . '_temp';
1070  
1071                  $statements[] = 'commit';
1072              }
1073          }
1074  
1075          if ($this->return_statements)
1076          {
1077              return $statements;
1078          }
1079      }
1080  
1081      /**
1082      * Gets a list of columns of a table.
1083      *
1084      * @param string $table        Table name
1085      *
1086      * @return array                Array of column names (all lower case)
1087      */
1088  	function sql_list_columns($table)
1089      {
1090          $columns = array();
1091  
1092          switch ($this->sql_layer)
1093          {
1094              case 'mysql_40':
1095              case 'mysql_41':
1096                  $sql = "SHOW COLUMNS FROM $table";
1097              break;
1098  
1099              // PostgreSQL has a way of doing this in a much simpler way but would
1100              // not allow us to support all versions of PostgreSQL
1101              case 'postgres':
1102                  $sql = "SELECT a.attname
1103                      FROM pg_class c, pg_attribute a
1104                      WHERE c.relname = '{$table}'
1105                          AND a.attnum > 0
1106                          AND a.attrelid = c.oid";
1107              break;
1108  
1109              // same deal with PostgreSQL, we must perform more complex operations than
1110              // we technically could
1111              case 'mssql':
1112              case 'mssqlnative':
1113                  $sql = "SELECT c.name
1114                      FROM syscolumns c
1115                      LEFT JOIN sysobjects o ON c.id = o.id
1116                      WHERE o.name = '{$table}'";
1117              break;
1118  
1119              case 'oracle':
1120                  $sql = "SELECT column_name
1121                      FROM user_tab_columns
1122                      WHERE LOWER(table_name) = '" . strtolower($table) . "'";
1123              break;
1124  
1125              case 'sqlite':
1126              case 'sqlite3':
1127                  $sql = "SELECT sql
1128                      FROM sqlite_master
1129                      WHERE type = 'table'
1130                          AND name = '{$table}'";
1131  
1132                  $result = $this->db->sql_query($sql);
1133  
1134                  if (!$result)
1135                  {
1136                      return false;
1137                  }
1138  
1139                  $row = $this->db->sql_fetchrow($result);
1140                  $this->db->sql_freeresult($result);
1141  
1142                  preg_match('#\((.*)\)#s', $row['sql'], $matches);
1143  
1144                  $cols = trim($matches[1]);
1145                  $col_array = preg_split('/,(?![\s\w]+\))/m', $cols);
1146  
1147                  foreach ($col_array as $declaration)
1148                  {
1149                      $entities = preg_split('#\s+#', trim($declaration));
1150                      if ($entities[0] == 'PRIMARY')
1151                      {
1152                          continue;
1153                      }
1154  
1155                      $column = strtolower($entities[0]);
1156                      $columns[$column] = $column;
1157                  }
1158  
1159                  return $columns;
1160              break;
1161          }
1162  
1163          $result = $this->db->sql_query($sql);
1164  
1165          while ($row = $this->db->sql_fetchrow($result))
1166          {
1167              $column = strtolower(current($row));
1168              $columns[$column] = $column;
1169          }
1170          $this->db->sql_freeresult($result);
1171  
1172          return $columns;
1173      }
1174  
1175      /**
1176      * Check whether a specified column exist in a table
1177      *
1178      * @param string    $table            Table to check
1179      * @param string    $column_name    Column to check
1180      *
1181      * @return bool        True if column exists, false otherwise
1182      */
1183  	function sql_column_exists($table, $column_name)
1184      {
1185          $columns = $this->sql_list_columns($table);
1186  
1187          return isset($columns[$column_name]);
1188      }
1189  
1190      /**
1191      * Check if a specified index exists in table. Does not return PRIMARY KEY and UNIQUE indexes.
1192      *
1193      * @param string    $table_name        Table to check the index at
1194      * @param string    $index_name        The index name to check
1195      *
1196      * @return bool True if index exists, else false
1197      */
1198  	function sql_index_exists($table_name, $index_name)
1199      {
1200          if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
1201          {
1202              $sql = "EXEC sp_statistics '$table_name'";
1203              $result = $this->db->sql_query($sql);
1204  
1205              while ($row = $this->db->sql_fetchrow($result))
1206              {
1207                  if ($row['TYPE'] == 3)
1208                  {
1209                      if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
1210                      {
1211                          $this->db->sql_freeresult($result);
1212                          return true;
1213                      }
1214                  }
1215              }
1216              $this->db->sql_freeresult($result);
1217  
1218              return false;
1219          }
1220  
1221          switch ($this->sql_layer)
1222          {
1223              case 'postgres':
1224                  $sql = "SELECT ic.relname as index_name
1225                      FROM pg_class bc, pg_class ic, pg_index i
1226                      WHERE (bc.oid = i.indrelid)
1227                          AND (ic.oid = i.indexrelid)
1228                          AND (bc.relname = '" . $table_name . "')
1229                          AND (i.indisunique != 't')
1230                          AND (i.indisprimary != 't')";
1231                  $col = 'index_name';
1232              break;
1233  
1234              case 'mysql_40':
1235              case 'mysql_41':
1236                  $sql = 'SHOW KEYS
1237                      FROM ' . $table_name;
1238                  $col = 'Key_name';
1239              break;
1240  
1241              case 'oracle':
1242                  $sql = "SELECT index_name
1243                      FROM user_indexes
1244                      WHERE table_name = '" . strtoupper($table_name) . "'
1245                          AND generated = 'N'
1246                          AND uniqueness = 'NONUNIQUE'";
1247                  $col = 'index_name';
1248              break;
1249  
1250              case 'sqlite':
1251              case 'sqlite3':
1252                  $sql = "PRAGMA index_list('" . $table_name . "');";
1253                  $col = 'name';
1254              break;
1255          }
1256  
1257          $result = $this->db->sql_query($sql);
1258          while ($row = $this->db->sql_fetchrow($result))
1259          {
1260              if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique'])
1261              {
1262                  continue;
1263              }
1264  
1265              // These DBMS prefix index name with the table name
1266              switch ($this->sql_layer)
1267              {
1268                  case 'oracle':
1269                  case 'postgres':
1270                  case 'sqlite':
1271                  case 'sqlite3':
1272                      $row[$col] = substr($row[$col], strlen($table_name) + 1);
1273                  break;
1274              }
1275  
1276              if (strtolower($row[$col]) == strtolower($index_name))
1277              {
1278                  $this->db->sql_freeresult($result);
1279                  return true;
1280              }
1281          }
1282          $this->db->sql_freeresult($result);
1283  
1284          return false;
1285      }
1286  
1287      /**
1288      * Check if a specified index exists in table. Does not return PRIMARY KEY indexes.
1289      *
1290      * @param string    $table_name        Table to check the index at
1291      * @param string    $index_name        The index name to check
1292      *
1293      * @return bool True if index exists, else false
1294      */
1295  	function sql_unique_index_exists($table_name, $index_name)
1296      {
1297          if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
1298          {
1299              $sql = "EXEC sp_statistics '$table_name'";
1300              $result = $this->db->sql_query($sql);
1301  
1302              while ($row = $this->db->sql_fetchrow($result))
1303              {
1304                  // Usually NON_UNIQUE is the column we want to check, but we allow for both
1305                  if ($row['TYPE'] == 3)
1306                  {
1307                      if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
1308                      {
1309                          $this->db->sql_freeresult($result);
1310                          return true;
1311                      }
1312                  }
1313              }
1314              $this->db->sql_freeresult($result);
1315              return false;
1316          }
1317  
1318          switch ($this->sql_layer)
1319          {
1320              case 'postgres':
1321                  $sql = "SELECT ic.relname as index_name, i.indisunique
1322                      FROM pg_class bc, pg_class ic, pg_index i
1323                      WHERE (bc.oid = i.indrelid)
1324                          AND (ic.oid = i.indexrelid)
1325                          AND (bc.relname = '" . $table_name . "')
1326                          AND (i.indisprimary != 't')";
1327                  $col = 'index_name';
1328              break;
1329  
1330              case 'mysql_40':
1331              case 'mysql_41':
1332                  $sql = 'SHOW KEYS
1333                      FROM ' . $table_name;
1334                  $col = 'Key_name';
1335              break;
1336  
1337              case 'oracle':
1338                  $sql = "SELECT index_name, table_owner
1339                      FROM user_indexes
1340                      WHERE table_name = '" . strtoupper($table_name) . "'
1341                          AND generated = 'N'
1342                          AND uniqueness = 'UNIQUE'";
1343                  $col = 'index_name';
1344              break;
1345  
1346              case 'sqlite':
1347              case 'sqlite3':
1348                  $sql = "PRAGMA index_list('" . $table_name . "');";
1349                  $col = 'name';
1350              break;
1351          }
1352  
1353          $result = $this->db->sql_query($sql);
1354          while ($row = $this->db->sql_fetchrow($result))
1355          {
1356              if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && ($row['Non_unique'] || $row[$col] == 'PRIMARY'))
1357              {
1358                  continue;
1359              }
1360  
1361              if (($this->sql_layer == 'sqlite' || $this->sql_layer == 'sqlite3') && !$row['unique'])
1362              {
1363                  continue;
1364              }
1365  
1366              if ($this->sql_layer == 'postgres' && $row['indisunique'] != 't')
1367              {
1368                  continue;
1369              }
1370  
1371              // These DBMS prefix index name with the table name
1372              switch ($this->sql_layer)
1373              {
1374                  case 'oracle':
1375                      // Two cases here... prefixed with U_[table_owner] and not prefixed with table_name
1376                      if (strpos($row[$col], 'U_') === 0)
1377                      {
1378                          $row[$col] = substr($row[$col], strlen('U_' . $row['table_owner']) + 1);
1379                      }
1380                      else if (strpos($row[$col], strtoupper($table_name)) === 0)
1381                      {
1382                          $row[$col] = substr($row[$col], strlen($table_name) + 1);
1383                      }
1384                  break;
1385  
1386                  case 'postgres':
1387                  case 'sqlite':
1388                  case 'sqlite3':
1389                      $row[$col] = substr($row[$col], strlen($table_name) + 1);
1390                  break;
1391              }
1392  
1393              if (strtolower($row[$col]) == strtolower($index_name))
1394              {
1395                  $this->db->sql_freeresult($result);
1396                  return true;
1397              }
1398          }
1399          $this->db->sql_freeresult($result);
1400  
1401          return false;
1402      }
1403  
1404      /**
1405      * Private method for performing sql statements (either execute them or return them)
1406      * @access private
1407      */
1408  	function _sql_run_sql($statements)
1409      {
1410          if ($this->return_statements)
1411          {
1412              return $statements;
1413          }
1414  
1415          // We could add error handling here...
1416          foreach ($statements as $sql)
1417          {
1418              if ($sql === 'begin')
1419              {
1420                  $this->db->sql_transaction('begin');
1421              }
1422              else if ($sql === 'commit')
1423              {
1424                  $this->db->sql_transaction('commit');
1425              }
1426              else
1427              {
1428                  $this->db->sql_query($sql);
1429              }
1430          }
1431  
1432          return true;
1433      }
1434  
1435      /**
1436      * Function to prepare some column information for better usage
1437      * @access private
1438      */
1439  	function sql_prepare_column_data($table_name, $column_name, $column_data)
1440      {
1441          if (strlen($column_name) > 30)
1442          {
1443              trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
1444          }
1445  
1446          // Get type
1447          list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
1448  
1449          // Adjust default value if db-dependent specified
1450          if (is_array($column_data[1]))
1451          {
1452              $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
1453          }
1454  
1455          $sql = '';
1456  
1457          $return_array = array();
1458  
1459          switch ($this->sql_layer)
1460          {
1461              case 'mssql':
1462              case 'mssqlnative':
1463                  $sql .= " {$column_type} ";
1464                  $sql_default = " {$column_type} ";
1465  
1466                  // For adding columns we need the default definition
1467                  if (!is_null($column_data[1]))
1468                  {
1469                      // For hexadecimal values do not use single quotes
1470                      if (strpos($column_data[1], '0x') === 0)
1471                      {
1472                          $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') ';
1473                          $sql_default .= $return_array['default'];
1474                      }
1475                      else
1476                      {
1477                          $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
1478                          $sql_default .= $return_array['default'];
1479                      }
1480                  }
1481  
1482                  if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
1483                  {
1484  //                    $sql .= 'IDENTITY (1, 1) ';
1485                      $sql_default .= 'IDENTITY (1, 1) ';
1486                  }
1487  
1488                  $return_array['textimage'] = $column_type === '[text]';
1489  
1490                  if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
1491                  {
1492                      $sql .= 'NOT NULL';
1493                      $sql_default .= 'NOT NULL';
1494                  }
1495                  else
1496                  {
1497                      $sql .= 'NULL';
1498                      $sql_default .= 'NULL';
1499                  }
1500  
1501                  $return_array['column_type_sql_default'] = $sql_default;
1502  
1503              break;
1504  
1505              case 'mysql_40':
1506              case 'mysql_41':
1507                  $sql .= " {$column_type} ";
1508  
1509                  // For hexadecimal values do not use single quotes
1510                  if (!is_null($column_data[1]) && substr($column_type, -4) !== 'text' && substr($column_type, -4) !== 'blob')
1511                  {
1512                      $sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' ";
1513                  }
1514  
1515                  if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
1516                  {
1517                      $sql .= 'NOT NULL';
1518                  }
1519                  else
1520                  {
1521                      $sql .= 'NULL';
1522                  }
1523  
1524                  if (isset($column_data[2]))
1525                  {
1526                      if ($column_data[2] == 'auto_increment')
1527                      {
1528                          $sql .= ' auto_increment';
1529                      }
1530                      else if ($this->sql_layer === 'mysql_41' && $column_data[2] == 'true_sort')
1531                      {
1532                          $sql .= ' COLLATE utf8_unicode_ci';
1533                      }
1534                  }
1535  
1536                  if (isset($column_data['after']))
1537                  {
1538                      $return_array['after'] = $column_data['after'];
1539                  }
1540  
1541              break;
1542  
1543              case 'oracle':
1544                  $sql .= " {$column_type} ";
1545                  $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : '';
1546  
1547                  // In Oracle empty strings ('') are treated as NULL.
1548                  // Therefore in oracle we allow NULL's for all DEFAULT '' entries
1549                  // Oracle does not like setting NOT NULL on a column that is already NOT NULL (this happens only on number fields)
1550                  if (!preg_match('/number/i', $column_type))
1551                  {
1552                      $sql .= ($column_data[1] === '' || $column_data[1] === null) ? '' : 'NOT NULL';
1553                  }
1554  
1555                  $return_array['auto_increment'] = false;
1556                  if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
1557                  {
1558                      $return_array['auto_increment'] = true;
1559                  }
1560  
1561              break;
1562  
1563              case 'postgres':
1564                  $return_array['column_type'] = $column_type;
1565  
1566                  $sql .= " {$column_type} ";
1567  
1568                  $return_array['auto_increment'] = false;
1569                  if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
1570                  {
1571                      $default_val = "nextval('{$table_name}_seq')";
1572                      $return_array['auto_increment'] = true;
1573                  }
1574                  else if (!is_null($column_data[1]))
1575                  {
1576                      $default_val = "'" . $column_data[1] . "'";
1577                      $return_array['null'] = 'NOT NULL';
1578                      $sql .= 'NOT NULL ';
1579                  }
1580                  else
1581                  {
1582                      // Integers need to have 0 instead of empty string as default
1583                      if (strpos($column_type, 'INT') === 0)
1584                      {
1585                          $default_val = '0';
1586                      }
1587                      else
1588                      {
1589                          $default_val = "'" . $column_data[1] . "'";
1590                      }
1591                      $return_array['null'] = 'NULL';
1592                      $sql .= 'NULL ';
1593                  }
1594  
1595                  $return_array['default'] = $default_val;
1596  
1597                  $sql .= "DEFAULT {$default_val}";
1598  
1599                  // Unsigned? Then add a CHECK contraint
1600                  if (in_array($orig_column_type, $this->unsigned_types))
1601                  {
1602                      $return_array['constraint'] = "CHECK ({$column_name} >= 0)";
1603                      $sql .= " CHECK ({$column_name} >= 0)";
1604                  }
1605  
1606              break;
1607  
1608              case 'sqlite':
1609              case 'sqlite3':
1610                  $return_array['primary_key_set'] = false;
1611                  if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
1612                  {
1613                      $sql .= ' INTEGER PRIMARY KEY';
1614                      $return_array['primary_key_set'] = true;
1615  
1616                      if ($this->sql_layer === 'sqlite3')
1617                      {
1618                          $sql .= ' AUTOINCREMENT';
1619                      }
1620                  }
1621                  else
1622                  {
1623                      $sql .= ' ' . $column_type;
1624                  }
1625  
1626                  if (!is_null($column_data[1]))
1627                  {
1628                      $sql .= ' NOT NULL ';
1629                      $sql .= "DEFAULT '{$column_data[1]}'";
1630                  }
1631  
1632              break;
1633          }
1634  
1635          $return_array['column_type_sql'] = $sql;
1636  
1637          return $return_array;
1638      }
1639  
1640      /**
1641      * Get the column's database type from the type map
1642      *
1643      * @param string $column_map_type
1644      * @return array        column type for this database
1645      *                    and map type without length
1646      */
1647  	function get_column_type($column_map_type)
1648      {
1649          if (strpos($column_map_type, ':') !== false)
1650          {
1651              list($orig_column_type, $column_length) = explode(':', $column_map_type);
1652              if (!is_array($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']))
1653              {
1654                  $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'], $column_length);
1655              }
1656              else
1657              {
1658                  if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule']))
1659                  {
1660                      switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][0])
1661                      {
1662                          case 'div':
1663                              $column_length /= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][1];
1664                              $column_length = ceil($column_length);
1665                              $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length);
1666                          break;
1667                      }
1668                  }
1669  
1670                  if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit']))
1671                  {
1672                      switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][0])
1673                      {
1674                          case 'mult':
1675                              $column_length *= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][1];
1676                              if ($column_length > $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][2])
1677                              {
1678                                  $column_type = $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][3];
1679                              }
1680                              else
1681                              {
1682                                  $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length);
1683                              }
1684                          break;
1685                      }
1686                  }
1687              }
1688              $orig_column_type .= ':';
1689          }
1690          else
1691          {
1692              $orig_column_type = $column_map_type;
1693              $column_type = $this->dbms_type_map[$this->sql_layer][$column_map_type];
1694          }
1695  
1696          return array($column_type, $orig_column_type);
1697      }
1698  
1699      /**
1700      * Add new column
1701      */
1702  	function sql_column_add($table_name, $column_name, $column_data, $inline = false)
1703      {
1704          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
1705          $statements = array();
1706  
1707          switch ($this->sql_layer)
1708          {
1709              case 'mssql':
1710              case 'mssqlnative':
1711                  // Does not support AFTER, only through temporary table
1712                  $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
1713              break;
1714  
1715              case 'mysql_40':
1716              case 'mysql_41':
1717                  $after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : '';
1718                  $statements[] = 'ALTER TABLE `' . $table_name . '` ADD COLUMN `' . $column_name . '` ' . $column_data['column_type_sql'] . $after;
1719              break;
1720  
1721              case 'oracle':
1722                  // Does not support AFTER, only through temporary table
1723                  $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql'];
1724              break;
1725  
1726              case 'postgres':
1727                  // Does not support AFTER, only through temporary table
1728                  if (version_compare($this->db->sql_server_info(true), '8.0', '>='))
1729                  {
1730                      $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql'];
1731                  }
1732                  else
1733                  {
1734                      // old versions cannot add columns with default and null information
1735                      $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint'];
1736  
1737                      if (isset($column_data['null']))
1738                      {
1739                          if ($column_data['null'] == 'NOT NULL')
1740                          {
1741                              $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL';
1742                          }
1743                      }
1744  
1745                      if (isset($column_data['default']))
1746                      {
1747                          $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
1748                      }
1749                  }
1750  
1751              break;
1752  
1753              case 'sqlite':
1754                  if ($inline && $this->return_statements)
1755                  {
1756                      return $column_name . ' ' . $column_data['column_type_sql'];
1757                  }
1758  
1759                  $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
1760                  if (empty($recreate_queries))
1761                  {
1762                      break;
1763                  }
1764  
1765                  $statements[] = 'begin';
1766  
1767                  $sql_create_table = array_shift($recreate_queries);
1768  
1769                  // Create a backup table and populate it, destroy the existing one
1770                  $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
1771                  $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
1772                  $statements[] = 'DROP TABLE ' . $table_name;
1773  
1774                  preg_match('#\((.*)\)#s', $sql_create_table, $matches);
1775  
1776                  $new_table_cols = trim($matches[1]);
1777                  $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
1778                  $column_list = array();
1779  
1780                  foreach ($old_table_cols as $declaration)
1781                  {
1782                      $entities = preg_split('#\s+#', trim($declaration));
1783                      if ($entities[0] == 'PRIMARY')
1784                      {
1785                          continue;
1786                      }
1787                      $column_list[] = $entities[0];
1788                  }
1789  
1790                  $columns = implode(',', $column_list);
1791  
1792                  $new_table_cols = $column_name . ' ' . $column_data['column_type_sql'] . ',' . $new_table_cols;
1793  
1794                  // create a new table and fill it up. destroy the temp one
1795                  $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');';
1796                  $statements = array_merge($statements, $recreate_queries);
1797  
1798                  $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
1799                  $statements[] = 'DROP TABLE ' . $table_name . '_temp';
1800  
1801                  $statements[] = 'commit';
1802              break;
1803  
1804              case 'sqlite3':
1805                  if ($inline && $this->return_statements)
1806                  {
1807                      return $column_name . ' ' . $column_data['column_type_sql'];
1808                  }
1809  
1810                  $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql'];
1811              break;
1812          }
1813  
1814          return $this->_sql_run_sql($statements);
1815      }
1816  
1817      /**
1818      * Drop column
1819      */
1820  	function sql_column_remove($table_name, $column_name, $inline = false)
1821      {
1822          $statements = array();
1823  
1824          switch ($this->sql_layer)
1825          {
1826              case 'mssql':
1827              case 'mssqlnative':
1828                  // We need the data here
1829                  $old_return_statements = $this->return_statements;
1830                  $this->return_statements = true;
1831  
1832                  $indexes = $this->get_existing_indexes($table_name, $column_name);
1833                  $indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true));
1834  
1835                  // Drop any indexes
1836                  $recreate_indexes = array();
1837                  if (!empty($indexes))
1838                  {
1839                      foreach ($indexes as $index_name => $index_data)
1840                      {
1841                          $result = $this->sql_index_drop($table_name, $index_name);
1842                          $statements = array_merge($statements, $result);
1843                          if (sizeof($index_data) > 1)
1844                          {
1845                              // Remove this column from the index and recreate it
1846                              $recreate_indexes[$index_name] = array_diff($index_data, array($column_name));
1847                          }
1848                      }
1849                  }
1850  
1851                  // Drop default value constraint
1852                  $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
1853                  $statements = array_merge($statements, $result);
1854  
1855                  // Remove the column
1856                  $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
1857  
1858                  if (!empty($recreate_indexes))
1859                  {
1860                      // Recreate indexes after we removed the column
1861                      foreach ($recreate_indexes as $index_name => $index_data)
1862                      {
1863                          $result = $this->sql_create_index($table_name, $index_name, $index_data);
1864                          $statements = array_merge($statements, $result);
1865                      }
1866                  }
1867  
1868                  $this->return_statements = $old_return_statements;
1869              break;
1870  
1871              case 'mysql_40':
1872              case 'mysql_41':
1873                  $statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`';
1874              break;
1875  
1876              case 'oracle':
1877                  $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name;
1878              break;
1879  
1880              case 'postgres':
1881                  $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
1882              break;
1883  
1884              case 'sqlite':
1885              case 'sqlite3':
1886  
1887                  if ($inline && $this->return_statements)
1888                  {
1889                      return $column_name;
1890                  }
1891  
1892                  $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name, $column_name);
1893                  if (empty($recreate_queries))
1894                  {
1895                      break;
1896                  }
1897  
1898                  $statements[] = 'begin';
1899  
1900                  $sql_create_table = array_shift($recreate_queries);
1901  
1902                  // Create a backup table and populate it, destroy the existing one
1903                  $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
1904                  $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
1905                  $statements[] = 'DROP TABLE ' . $table_name;
1906  
1907                  preg_match('#\((.*)\)#s', $sql_create_table, $matches);
1908  
1909                  $new_table_cols = trim($matches[1]);
1910                  $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
1911                  $column_list = array();
1912  
1913                  foreach ($old_table_cols as $declaration)
1914                  {
1915                      $entities = preg_split('#\s+#', trim($declaration));
1916                      if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name)
1917                      {
1918                          continue;
1919                      }
1920                      $column_list[] = $entities[0];
1921                  }
1922  
1923                  $columns = implode(',', $column_list);
1924  
1925                  $new_table_cols = trim(preg_replace('/' . $column_name . '\b[^,]+(?:,|$)/m', '', $new_table_cols));
1926                  if (substr($new_table_cols, -1) === ',')
1927                  {
1928                      // Remove the comma from the last entry again
1929                      $new_table_cols = substr($new_table_cols, 0, -1);
1930                  }
1931  
1932                  // create a new table and fill it up. destroy the temp one
1933                  $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');';
1934                  $statements = array_merge($statements, $recreate_queries);
1935  
1936                  $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
1937                  $statements[] = 'DROP TABLE ' . $table_name . '_temp';
1938  
1939                  $statements[] = 'commit';
1940              break;
1941          }
1942  
1943          return $this->_sql_run_sql($statements);
1944      }
1945  
1946      /**
1947      * Drop Index
1948      */
1949  	function sql_index_drop($table_name, $index_name)
1950      {
1951          $statements = array();
1952  
1953          switch ($this->sql_layer)
1954          {
1955              case 'mssql':
1956              case 'mssqlnative':
1957                  $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
1958              break;
1959  
1960              case 'mysql_40':
1961              case 'mysql_41':
1962                  $statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name;
1963              break;
1964  
1965              case 'oracle':
1966              case 'postgres':
1967              case 'sqlite':
1968              case 'sqlite3':
1969                  $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
1970              break;
1971          }
1972  
1973          return $this->_sql_run_sql($statements);
1974      }
1975  
1976      /**
1977      * Drop Table
1978      */
1979  	function sql_table_drop($table_name)
1980      {
1981          $statements = array();
1982  
1983          if (!$this->sql_table_exists($table_name))
1984          {
1985              return $this->_sql_run_sql($statements);
1986          }
1987  
1988          // the most basic operation, get rid of the table
1989          $statements[] = 'DROP TABLE ' . $table_name;
1990  
1991          switch ($this->sql_layer)
1992          {
1993              case 'oracle':
1994                  $sql = 'SELECT A.REFERENCED_NAME
1995                      FROM USER_DEPENDENCIES A, USER_TRIGGERS B
1996                      WHERE A.REFERENCED_TYPE = \'SEQUENCE\'
1997                          AND A.NAME = B.TRIGGER_NAME
1998                          AND B.TABLE_NAME = \'' . strtoupper($table_name) . "'";
1999                  $result = $this->db->sql_query($sql);
2000  
2001                  // any sequences ref'd to this table's triggers?
2002                  while ($row = $this->db->sql_fetchrow($result))
2003                  {
2004                      $statements[] = "DROP SEQUENCE {$row['referenced_name']}";
2005                  }
2006                  $this->db->sql_freeresult($result);
2007              break;
2008  
2009              case 'postgres':
2010                  // PGSQL does not "tightly" bind sequences and tables, we must guess...
2011                  $sql = "SELECT relname
2012                      FROM pg_class
2013                      WHERE relkind = 'S'
2014                          AND relname = '{$table_name}_seq'";
2015                  $result = $this->db->sql_query($sql);
2016  
2017                  // We don't even care about storing the results. We already know the answer if we get rows back.
2018                  if ($this->db->sql_fetchrow($result))
2019                  {
2020                      $statements[] =  "DROP SEQUENCE {$table_name}_seq;\n";
2021                  }
2022                  $this->db->sql_freeresult($result);
2023              break;
2024          }
2025  
2026          return $this->_sql_run_sql($statements);
2027      }
2028  
2029      /**
2030      * Add primary key
2031      */
2032  	function sql_create_primary_key($table_name, $column, $inline = false)
2033      {
2034          $statements = array();
2035  
2036          switch ($this->sql_layer)
2037          {
2038              case 'postgres':
2039              case 'mysql_40':
2040              case 'mysql_41':
2041                  $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
2042              break;
2043  
2044              case 'mssql':
2045              case 'mssqlnative':
2046                  $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
2047                  $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY  CLUSTERED (";
2048                  $sql .= '[' . implode("],\n\t\t[", $column) . ']';
2049                  $sql .= ')';
2050  
2051                  $statements[] = $sql;
2052              break;
2053  
2054              case 'oracle':
2055                  $statements[] = 'ALTER TABLE ' . $table_name . ' add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')';
2056              break;
2057  
2058              case 'sqlite':
2059              case 'sqlite3':
2060  
2061                  if ($inline && $this->return_statements)
2062                  {
2063                      return $column;
2064                  }
2065  
2066                  $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
2067                  if (empty($recreate_queries))
2068                  {
2069                      break;
2070                  }
2071  
2072                  $statements[] = 'begin';
2073  
2074                  $sql_create_table = array_shift($recreate_queries);
2075  
2076                  // Create a backup table and populate it, destroy the existing one
2077                  $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
2078                  $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
2079                  $statements[] = 'DROP TABLE ' . $table_name;
2080  
2081                  preg_match('#\((.*)\)#s', $sql_create_table, $matches);
2082  
2083                  $new_table_cols = trim($matches[1]);
2084                  $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
2085                  $column_list = array();
2086  
2087                  foreach ($old_table_cols as $declaration)
2088                  {
2089                      $entities = preg_split('#\s+#', trim($declaration));
2090                      if ($entities[0] == 'PRIMARY')
2091                      {
2092                          continue;
2093                      }
2094                      $column_list[] = $entities[0];
2095                  }
2096  
2097                  $columns = implode(',', $column_list);
2098  
2099                  // create a new table and fill it up. destroy the temp one
2100                  $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ', PRIMARY KEY (' . implode(', ', $column) . '));';
2101                  $statements = array_merge($statements, $recreate_queries);
2102  
2103                  $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
2104                  $statements[] = 'DROP TABLE ' . $table_name . '_temp';
2105  
2106                  $statements[] = 'commit';
2107              break;
2108          }
2109  
2110          return $this->_sql_run_sql($statements);
2111      }
2112  
2113      /**
2114      * Add unique index
2115      */
2116  	function sql_create_unique_index($table_name, $index_name, $column)
2117      {
2118          $statements = array();
2119  
2120          $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config)
2121          if (strlen($table_name . '_' . $index_name) - strlen($table_prefix) > 24)
2122          {
2123              $max_length = strlen($table_prefix) + 24;
2124              trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR);
2125          }
2126  
2127          switch ($this->sql_layer)
2128          {
2129              case 'postgres':
2130              case 'oracle':
2131              case 'sqlite':
2132              case 'sqlite3':
2133                  $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
2134              break;
2135  
2136              case 'mysql_40':
2137              case 'mysql_41':
2138                  $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')';
2139              break;
2140  
2141              case 'mssql':
2142              case 'mssqlnative':
2143                  $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
2144              break;
2145          }
2146  
2147          return $this->_sql_run_sql($statements);
2148      }
2149  
2150      /**
2151      * Add index
2152      */
2153  	function sql_create_index($table_name, $index_name, $column)
2154      {
2155          $statements = array();
2156  
2157          $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config)
2158          if (strlen($table_name . $index_name) - strlen($table_prefix) > 24)
2159          {
2160              $max_length = strlen($table_prefix) + 24;
2161              trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR);
2162          }
2163  
2164          // remove index length unless MySQL4
2165          if ('mysql_40' != $this->sql_layer)
2166          {
2167              $column = preg_replace('#:.*$#', '', $column);
2168          }
2169  
2170          switch ($this->sql_layer)
2171          {
2172              case 'postgres':
2173              case 'oracle':
2174              case 'sqlite':
2175              case 'sqlite3':
2176                  $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
2177              break;
2178  
2179              case 'mysql_40':
2180                  // add index size to definition as required by MySQL4
2181                  foreach ($column as $i => $col)
2182                  {
2183                      if (false !== strpos($col, ':'))
2184                      {
2185                          list($col, $index_size) = explode(':', $col);
2186                          $column[$i] = "$col($index_size)";
2187                      }
2188                  }
2189              // no break
2190              case 'mysql_41':
2191                  $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . ' (' . implode(', ', $column) . ')';
2192              break;
2193  
2194              case 'mssql':
2195              case 'mssqlnative':
2196                  $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
2197              break;
2198          }
2199  
2200          return $this->_sql_run_sql($statements);
2201      }
2202  
2203      /**
2204      * List all of the indices that belong to a table,
2205      * does not count:
2206      * * UNIQUE indices
2207      * * PRIMARY keys
2208      */
2209  	function sql_list_index($table_name)
2210      {
2211          $index_array = array();
2212  
2213          if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
2214          {
2215              $sql = "EXEC sp_statistics '$table_name'";
2216              $result = $this->db->sql_query($sql);
2217              while ($row = $this->db->sql_fetchrow($result))
2218              {
2219                  if ($row['TYPE'] == 3)
2220                  {
2221                      $index_array[] = $row['INDEX_NAME'];
2222                  }
2223              }
2224              $this->db->sql_freeresult($result);
2225          }
2226          else
2227          {
2228              switch ($this->sql_layer)
2229              {
2230                  case 'postgres':
2231                      $sql = "SELECT ic.relname as index_name
2232                          FROM pg_class bc, pg_class ic, pg_index i
2233                          WHERE (bc.oid = i.indrelid)
2234                              AND (ic.oid = i.indexrelid)
2235                              AND (bc.relname = '" . $table_name . "')
2236                              AND (i.indisunique != 't')
2237                              AND (i.indisprimary != 't')";
2238                      $col = 'index_name';
2239                  break;
2240  
2241                  case 'mysql_40':
2242                  case 'mysql_41':
2243                      $sql = 'SHOW KEYS
2244                          FROM ' . $table_name;
2245                      $col = 'Key_name';
2246                  break;
2247  
2248                  case 'oracle':
2249                      $sql = "SELECT index_name
2250                          FROM user_indexes
2251                          WHERE table_name = '" . strtoupper($table_name) . "'
2252                              AND generated = 'N'
2253                              AND uniqueness = 'NONUNIQUE'";
2254                      $col = 'index_name';
2255                  break;
2256  
2257                  case 'sqlite':
2258                  case 'sqlite3':
2259                      $sql = "PRAGMA index_info('" . $table_name . "');";
2260                      $col = 'name';
2261                  break;
2262              }
2263  
2264              $result = $this->db->sql_query($sql);
2265              while ($row = $this->db->sql_fetchrow($result))
2266              {
2267                  if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique'])
2268                  {
2269                      continue;
2270                  }
2271  
2272                  switch ($this->sql_layer)
2273                  {
2274                      case 'oracle':
2275                      case 'postgres':
2276                      case 'sqlite':
2277                      case 'sqlite3':
2278                          $row[$col] = substr($row[$col], strlen($table_name) + 1);
2279                      break;
2280                  }
2281  
2282                  $index_array[] = $row[$col];
2283              }
2284              $this->db->sql_freeresult($result);
2285          }
2286  
2287          return array_map('strtolower', $index_array);
2288      }
2289  
2290      /**
2291       * Removes table_name from the index_name if it is at the beginning
2292       *
2293       * @param $table_name
2294       * @param $index_name
2295       * @return string
2296       */
2297  	protected function strip_table_name_from_index_name($table_name, $index_name)
2298      {
2299          return (strpos(strtoupper($index_name), strtoupper($table_name)) === 0) ? substr($index_name, strlen($table_name) + 1) : $index_name;
2300      }
2301  
2302      /**
2303      * Change column type (not name!)
2304      */
2305  	function sql_column_change($table_name, $column_name, $column_data, $inline = false)
2306      {
2307          $original_column_data = $column_data;
2308          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
2309          $statements = array();
2310  
2311          switch ($this->sql_layer)
2312          {
2313              case 'mssql':
2314              case 'mssqlnative':
2315                  // We need the data here
2316                  $old_return_statements = $this->return_statements;
2317                  $this->return_statements = true;
2318  
2319                  $indexes = $this->get_existing_indexes($table_name, $column_name);
2320                  $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
2321  
2322                  // Drop any indexes
2323                  if (!empty($indexes) || !empty($unique_indexes))
2324                  {
2325                      $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
2326                      foreach ($drop_indexes as $index_name)
2327                      {
2328                          $result = $this->sql_index_drop($table_name, $index_name);
2329                          $statements = array_merge($statements, $result);
2330                      }
2331                  }
2332  
2333                  // Drop default value constraint
2334                  $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
2335                  $statements = array_merge($statements, $result);
2336  
2337                  // Change the column
2338                  $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
2339  
2340                  if (!empty($column_data['default']))
2341                  {
2342                      // Add new default value constraint
2343                      $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $column_data['default'] . ' FOR [' . $column_name . ']';
2344                  }
2345  
2346                  if (!empty($indexes))
2347                  {
2348                      // Recreate indexes after we changed the column
2349                      foreach ($indexes as $index_name => $index_data)
2350                      {
2351                          $result = $this->sql_create_index($table_name, $index_name, $index_data);
2352                          $statements = array_merge($statements, $result);
2353                      }
2354                  }
2355  
2356                  if (!empty($unique_indexes))
2357                  {
2358                      // Recreate unique indexes after we changed the column
2359                      foreach ($unique_indexes as $index_name => $index_data)
2360                      {
2361                          $result = $this->sql_create_unique_index($table_name, $index_name, $index_data);
2362                          $statements = array_merge($statements, $result);
2363                      }
2364                  }
2365  
2366                  $this->return_statements = $old_return_statements;
2367              break;
2368  
2369              case 'mysql_40':
2370              case 'mysql_41':
2371                  $statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql'];
2372              break;
2373  
2374              case 'oracle':
2375                  // We need the data here
2376                  $old_return_statements = $this->return_statements;
2377                  $this->return_statements = true;
2378  
2379                  // Get list of existing indexes
2380                  $indexes = $this->get_existing_indexes($table_name, $column_name);
2381                  $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
2382  
2383                  // Drop any indexes
2384                  if (!empty($indexes) || !empty($unique_indexes))
2385                  {
2386                      $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
2387                      foreach ($drop_indexes as $index_name)
2388                      {
2389                          $result = $this->sql_index_drop($table_name, $this->strip_table_name_from_index_name($table_name, $index_name));
2390                          $statements = array_merge($statements, $result);
2391                      }
2392                  }
2393  
2394                  $temp_column_name = 'temp_' . substr(md5($column_name), 0, 25);
2395                  // Add a temporary table with the new type
2396                  $result = $this->sql_column_add($table_name, $temp_column_name, $original_column_data);
2397                  $statements = array_merge($statements, $result);
2398  
2399                  // Copy the data to the new column
2400                  $statements[] = 'UPDATE ' . $table_name . ' SET ' . $temp_column_name . ' = ' . $column_name;
2401  
2402                  // Drop the original column
2403                  $result = $this->sql_column_remove($table_name, $column_name);
2404                  $statements = array_merge($statements, $result);
2405  
2406                  // Recreate the original column with the new type
2407                  $result = $this->sql_column_add($table_name, $column_name, $original_column_data);
2408                  $statements = array_merge($statements, $result);
2409  
2410                  if (!empty($indexes))
2411                  {
2412                      // Recreate indexes after we changed the column
2413                      foreach ($indexes as $index_name => $index_data)
2414                      {
2415                          $result = $this->sql_create_index($table_name, $this->strip_table_name_from_index_name($table_name, $index_name), $index_data);
2416                          $statements = array_merge($statements, $result);
2417                      }
2418                  }
2419  
2420                  if (!empty($unique_indexes))
2421                  {
2422                      // Recreate unique indexes after we changed the column
2423                      foreach ($unique_indexes as $index_name => $index_data)
2424                      {
2425                          $result = $this->sql_create_unique_index($table_name, $this->strip_table_name_from_index_name($table_name, $index_name), $index_data);
2426                          $statements = array_merge($statements, $result);
2427                      }
2428                  }
2429  
2430                  // Copy the data to the original column
2431                  $statements[] = 'UPDATE ' . $table_name . ' SET ' . $column_name . ' = ' . $temp_column_name;
2432  
2433                  // Drop the temporary column again
2434                  $result = $this->sql_column_remove($table_name, $temp_column_name);
2435                  $statements = array_merge($statements, $result);
2436  
2437                  $this->return_statements = $old_return_statements;
2438              break;
2439  
2440              case 'postgres':
2441                  $sql = 'ALTER TABLE ' . $table_name . ' ';
2442  
2443                  $sql_array = array();
2444                  $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
2445  
2446                  if (isset($column_data['null']))
2447                  {
2448                      if ($column_data['null'] == 'NOT NULL')
2449                      {
2450                          $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
2451                      }
2452                      else if ($column_data['null'] == 'NULL')
2453                      {
2454                          $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
2455                      }
2456                  }
2457  
2458                  if (isset($column_data['default']))
2459                  {
2460                      $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
2461                  }
2462  
2463                  // we don't want to double up on constraints if we change different number data types
2464                  if (isset($column_data['constraint']))
2465                  {
2466                      $constraint_sql = "SELECT consrc as constraint_data
2467                                  FROM pg_constraint, pg_class bc
2468                                  WHERE conrelid = bc.oid
2469                                      AND bc.relname = '{$table_name}'
2470                                      AND NOT EXISTS (
2471                                          SELECT *
2472                                              FROM pg_constraint as c, pg_inherits as i
2473                                              WHERE i.inhrelid = pg_constraint.conrelid
2474                                                  AND c.conname = pg_constraint.conname
2475                                                  AND c.consrc = pg_constraint.consrc
2476                                                  AND c.conrelid = i.inhparent
2477                                      )";
2478  
2479                      $constraint_exists = false;
2480  
2481                      $result = $this->db->sql_query($constraint_sql);
2482                      while ($row = $this->db->sql_fetchrow($result))
2483                      {
2484                          if (trim($row['constraint_data']) == trim($column_data['constraint']))
2485                          {
2486                              $constraint_exists = true;
2487                              break;
2488                          }
2489                      }
2490                      $this->db->sql_freeresult($result);
2491  
2492                      if (!$constraint_exists)
2493                      {
2494                          $sql_array[] = 'ADD ' . $column_data['constraint'];
2495                      }
2496                  }
2497  
2498                  $sql .= implode(', ', $sql_array);
2499  
2500                  $statements[] = $sql;
2501              break;
2502  
2503              case 'sqlite':
2504              case 'sqlite3':
2505  
2506                  if ($inline && $this->return_statements)
2507                  {
2508                      return $column_name . ' ' . $column_data['column_type_sql'];
2509                  }
2510  
2511                  $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
2512                  if (empty($recreate_queries))
2513                  {
2514                      break;
2515                  }
2516  
2517                  $statements[] = 'begin';
2518  
2519                  $sql_create_table = array_shift($recreate_queries);
2520  
2521                  // Create a temp table and populate it, destroy the existing one
2522                  $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
2523                  $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
2524                  $statements[] = 'DROP TABLE ' . $table_name;
2525  
2526                  preg_match('#\((.*)\)#s', $sql_create_table, $matches);
2527  
2528                  $new_table_cols = trim($matches[1]);
2529                  $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
2530                  $column_list = array();
2531  
2532                  foreach ($old_table_cols as $key => $declaration)
2533                  {
2534                      $declaration = trim($declaration);
2535  
2536                      // Check for the beginning of the constraint section and stop
2537                      if (preg_match('/[^\(]*\s*PRIMARY KEY\s+\(/', $declaration) ||
2538                          preg_match('/[^\(]*\s*UNIQUE\s+\(/', $declaration) ||
2539                          preg_match('/[^\(]*\s*FOREIGN KEY\s+\(/', $declaration) ||
2540                          preg_match('/[^\(]*\s*CHECK\s+\(/', $declaration))
2541                      {
2542                          break;
2543                      }
2544  
2545                      $entities = preg_split('#\s+#', $declaration);
2546                      $column_list[] = $entities[0];
2547                      if ($entities[0] == $column_name)
2548                      {
2549                          $old_table_cols[$key] = $column_name . ' ' . $column_data['column_type_sql'];
2550                      }
2551                  }
2552  
2553                  $columns = implode(',', $column_list);
2554  
2555                  // Create a new table and fill it up. destroy the temp one
2556                  $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $old_table_cols) . ');';
2557                  $statements = array_merge($statements, $recreate_queries);
2558  
2559                  $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
2560                  $statements[] = 'DROP TABLE ' . $table_name . '_temp';
2561  
2562                  $statements[] = 'commit';
2563  
2564              break;
2565          }
2566  
2567          return $this->_sql_run_sql($statements);
2568      }
2569  
2570      /**
2571      * Get queries to drop the default constraints of a column
2572      *
2573      * We need to drop the default constraints of a column,
2574      * before being able to change their type or deleting them.
2575      *
2576      * @param string $table_name
2577      * @param string $column_name
2578      * @return array        Array with SQL statements
2579      */
2580  	protected function mssql_get_drop_default_constraints_queries($table_name, $column_name)
2581      {
2582          $statements = array();
2583          if ($this->mssql_is_sql_server_2000())
2584          {
2585              // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
2586              // Deprecated in SQL Server 2005
2587              $sql = "SELECT so.name AS def_name
2588                  FROM sysobjects so
2589                  JOIN sysconstraints sc ON so.id = sc.constid
2590                  WHERE object_name(so.parent_obj) = '{$table_name}'
2591                      AND so.xtype = 'D'
2592                      AND sc.colid = (SELECT colid FROM syscolumns
2593                          WHERE id = object_id('{$table_name}')
2594                              AND name = '{$column_name}')";
2595          }
2596          else
2597          {
2598              $sql = "SELECT dobj.name AS def_name
2599                  FROM sys.columns col
2600                      LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D')
2601                  WHERE col.object_id = object_id('{$table_name}')
2602                      AND col.name = '{$column_name}'
2603                      AND dobj.name IS NOT NULL";
2604          }
2605  
2606          $result = $this->db->sql_query($sql);
2607          while ($row = $this->db->sql_fetchrow($result))
2608          {
2609              $statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']';
2610          }
2611          $this->db->sql_freeresult($result);
2612  
2613          return $statements;
2614      }
2615  
2616      /**
2617      * Get a list with existing indexes for the column
2618      *
2619      * @param string $table_name
2620      * @param string $column_name
2621      * @param bool $unique Should we get unique indexes or normal ones
2622      * @return array        Array with Index name => columns
2623      */
2624  	public function get_existing_indexes($table_name, $column_name, $unique = false)
2625      {
2626          switch ($this->sql_layer)
2627          {
2628              case 'mysql_40':
2629              case 'mysql_41':
2630              case 'postgres':
2631              case 'sqlite':
2632              case 'sqlite3':
2633                  // Not supported
2634                  throw new \Exception('DBMS is not supported');
2635              break;
2636          }
2637  
2638          $sql = '';
2639          $existing_indexes = array();
2640  
2641          switch ($this->sql_layer)
2642          {
2643              case 'mssql':
2644              case 'mssqlnative':
2645                  if ($this->mssql_is_sql_server_2000())
2646                  {
2647                      // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
2648                      // Deprecated in SQL Server 2005
2649                      $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
2650                      FROM sysindexes ix
2651                      INNER JOIN sysindexkeys ixc
2652                          ON ixc.id = ix.id
2653                              AND ixc.indid = ix.indid
2654                      INNER JOIN syscolumns cols
2655                          ON cols.colid = ixc.colid
2656                              AND cols.id = ix.id
2657                      WHERE ix.id = object_id('{$table_name}')
2658                          AND cols.name = '{$column_name}'
2659                          AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0');
2660                  }
2661                  else
2662                  {
2663                      $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
2664                      FROM sys.indexes ix
2665                      INNER JOIN sys.index_columns ixc
2666                          ON ixc.object_id = ix.object_id
2667                              AND ixc.index_id = ix.index_id
2668                      INNER JOIN sys.columns cols
2669                          ON cols.column_id = ixc.column_id
2670                              AND cols.object_id = ix.object_id
2671                      WHERE ix.object_id = object_id('{$table_name}')
2672                          AND cols.name = '{$column_name}'
2673                          AND ix.is_unique = " . ($unique ? '1' : '0');
2674                  }
2675              break;
2676  
2677              case 'oracle':
2678                  $sql = "SELECT ix.index_name  AS phpbb_index_name, ix.uniqueness AS is_unique
2679                      FROM all_ind_columns ixc, all_indexes ix
2680                      WHERE ix.index_name = ixc.index_name
2681                          AND ixc.table_name = '" . strtoupper($table_name) . "'
2682                          AND ixc.column_name = '" . strtoupper($column_name) . "'";
2683              break;
2684          }
2685  
2686          $result = $this->db->sql_query($sql);
2687          while ($row = $this->db->sql_fetchrow($result))
2688          {
2689              if (!isset($row['is_unique']) || ($unique && $row['is_unique'] == 'UNIQUE') || (!$unique && $row['is_unique'] == 'NONUNIQUE'))
2690              {
2691                  $existing_indexes[$row['phpbb_index_name']] = array();
2692              }
2693          }
2694          $this->db->sql_freeresult($result);
2695  
2696          if (empty($existing_indexes))
2697          {
2698              return array();
2699          }
2700  
2701          switch ($this->sql_layer)
2702          {
2703              case 'mssql':
2704              case 'mssqlnative':
2705                  if ($this->mssql_is_sql_server_2000())
2706                  {
2707                      $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
2708                          FROM sysindexes ix
2709                          INNER JOIN sysindexkeys ixc
2710                              ON ixc.id = ix.id
2711                                  AND ixc.indid = ix.indid
2712                          INNER JOIN syscolumns cols
2713                              ON cols.colid = ixc.colid
2714                                  AND cols.id = ix.id
2715                          WHERE ix.id = object_id('{$table_name}')
2716                              AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
2717                  }
2718                  else
2719                  {
2720                      $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
2721                          FROM sys.indexes ix
2722                          INNER JOIN sys.index_columns ixc
2723                              ON ixc.object_id = ix.object_id
2724                                  AND ixc.index_id = ix.index_id
2725                          INNER JOIN sys.columns cols
2726                              ON cols.column_id = ixc.column_id
2727                                  AND cols.object_id = ix.object_id
2728                          WHERE ix.object_id = object_id('{$table_name}')
2729                              AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
2730                  }
2731              break;
2732  
2733              case 'oracle':
2734                  $sql = "SELECT index_name AS phpbb_index_name, column_name AS phpbb_column_name
2735                      FROM all_ind_columns
2736                      WHERE table_name = '" . strtoupper($table_name) . "'
2737                          AND " . $this->db->sql_in_set('index_name', array_keys($existing_indexes));
2738              break;
2739          }
2740  
2741          $result = $this->db->sql_query($sql);
2742          while ($row = $this->db->sql_fetchrow($result))
2743          {
2744              $existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name'];
2745          }
2746          $this->db->sql_freeresult($result);
2747  
2748          return $existing_indexes;
2749      }
2750  
2751      /**
2752      * Is the used MS SQL Server a SQL Server 2000?
2753      *
2754      * @return bool
2755      */
2756  	protected function mssql_is_sql_server_2000()
2757      {
2758          if ($this->is_sql_server_2000 === null)
2759          {
2760              $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version";
2761              $result = $this->db->sql_query($sql);
2762              $properties = $this->db->sql_fetchrow($result);
2763              $this->db->sql_freeresult($result);
2764              $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8';
2765          }
2766  
2767          return $this->is_sql_server_2000;
2768      }
2769  
2770      /**
2771      * Returns the Queries which are required to recreate a table including indexes
2772      *
2773      * @param string $table_name
2774      * @param string $remove_column    When we drop a column, we remove the column
2775      *                                from all indexes. If the index has no other
2776      *                                column, we drop it completly.
2777      * @return array
2778      */
2779  	protected function sqlite_get_recreate_table_queries($table_name, $remove_column = '')
2780      {
2781          $queries = array();
2782  
2783          $sql = "SELECT sql
2784              FROM sqlite_master
2785              WHERE type = 'table'
2786                  AND name = '{$table_name}'";
2787          $result = $this->db->sql_query($sql);
2788          $sql_create_table = $this->db->sql_fetchfield('sql');
2789          $this->db->sql_freeresult($result);
2790  
2791          if (!$sql_create_table)
2792          {
2793              return array();
2794          }
2795          $queries[] = $sql_create_table;
2796  
2797          $sql = "SELECT sql
2798              FROM sqlite_master
2799              WHERE type = 'index'
2800                  AND tbl_name = '{$table_name}'";
2801          $result = $this->db->sql_query($sql);
2802          while ($sql_create_index = $this->db->sql_fetchfield('sql'))
2803          {
2804              if ($remove_column)
2805              {
2806                  $match = array();
2807                  preg_match('#(?:[\w ]+)\((.*)\)#', $sql_create_index, $match);
2808                  if (!isset($match[1]))
2809                  {
2810                      continue;
2811                  }
2812  
2813                  // Find and remove $remove_column from the index
2814                  $columns = explode(', ', $match[1]);
2815                  $found_column = array_search($remove_column, $columns);
2816                  if ($found_column !== false)
2817                  {
2818                      unset($columns[$found_column]);
2819  
2820                      // If the column list is not empty add the index to the list
2821                      if (!empty($columns))
2822                      {
2823                          $queries[] = str_replace($match[1], implode(', ', $columns), $sql_create_index);
2824                      }
2825                  }
2826                  else
2827                  {
2828                      $queries[] = $sql_create_index;
2829                  }
2830              }
2831              else
2832              {
2833                  $queries[] = $sql_create_index;
2834              }
2835          }
2836          $this->db->sql_freeresult($result);
2837  
2838          return $queries;
2839      }
2840  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1