[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/phpbb/db/tools/ -> postgres.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\tools;
  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 postgres extends tools
  21  {
  22      /**
  23       * Get the column types for postgres only
  24       *
  25       * @return array
  26       */
  27  	public static function get_dbms_type_map()
  28      {
  29          return array(
  30              'postgres'    => array(
  31                  'INT:'        => 'INT4',
  32                  'BINT'        => 'INT8',
  33                  'ULINT'        => 'INT4', // unsigned
  34                  'UINT'        => 'INT4', // unsigned
  35                  'UINT:'        => 'INT4', // unsigned
  36                  'USINT'        => 'INT2', // unsigned
  37                  'BOOL'        => 'INT2', // unsigned
  38                  'TINT:'        => 'INT2',
  39                  'VCHAR'        => 'varchar(255)',
  40                  'VCHAR:'    => 'varchar(%d)',
  41                  'CHAR:'        => 'char(%d)',
  42                  'XSTEXT'    => 'varchar(1000)',
  43                  'STEXT'        => 'varchar(3000)',
  44                  'TEXT'        => 'varchar(8000)',
  45                  'MTEXT'        => 'TEXT',
  46                  'XSTEXT_UNI'=> 'varchar(100)',
  47                  'STEXT_UNI'    => 'varchar(255)',
  48                  'TEXT_UNI'    => 'varchar(4000)',
  49                  'MTEXT_UNI'    => 'TEXT',
  50                  'TIMESTAMP'    => 'INT4', // unsigned
  51                  'DECIMAL'    => 'decimal(5,2)',
  52                  'DECIMAL:'    => 'decimal(%d,2)',
  53                  'PDECIMAL'    => 'decimal(6,3)',
  54                  'PDECIMAL:'    => 'decimal(%d,3)',
  55                  'VCHAR_UNI'    => 'varchar(255)',
  56                  'VCHAR_UNI:'=> 'varchar(%d)',
  57                  'VCHAR_CI'    => 'varchar_ci',
  58                  'VARBINARY'    => 'bytea',
  59              ),
  60          );
  61      }
  62  
  63      /**
  64      * Constructor. Set DB Object and set {@link $return_statements return_statements}.
  65      *
  66      * @param \phpbb\db\driver\driver_interface    $db                    Database connection
  67      * @param bool        $return_statements    True if only statements should be returned and no SQL being executed
  68      */
  69  	public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
  70      {
  71          parent::__construct($db, $return_statements);
  72  
  73          // Determine mapping database type
  74          $this->sql_layer = 'postgres';
  75  
  76          $this->dbms_type_map = self::get_dbms_type_map();
  77      }
  78  
  79      /**
  80       * {@inheritDoc}
  81       */
  82  	function sql_list_tables()
  83      {
  84          $sql = 'SELECT relname
  85              FROM pg_stat_user_tables';
  86          $result = $this->db->sql_query($sql);
  87  
  88          $tables = array();
  89          while ($row = $this->db->sql_fetchrow($result))
  90          {
  91              $name = current($row);
  92              $tables[$name] = $name;
  93          }
  94          $this->db->sql_freeresult($result);
  95  
  96          return $tables;
  97      }
  98  
  99      /**
 100       * {@inheritDoc}
 101       */
 102  	function sql_create_table($table_name, $table_data)
 103      {
 104          // holds the DDL for a column
 105          $columns = $statements = array();
 106  
 107          if ($this->sql_table_exists($table_name))
 108          {
 109              return $this->_sql_run_sql($statements);
 110          }
 111  
 112          // Begin transaction
 113          $statements[] = 'begin';
 114  
 115          // Determine if we have created a PRIMARY KEY in the earliest
 116          $primary_key_gen = false;
 117  
 118          // Determine if the table requires a sequence
 119          $create_sequence = false;
 120  
 121          // Begin table sql statement
 122          $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
 123  
 124          // Iterate through the columns to create a table
 125          foreach ($table_data['COLUMNS'] as $column_name => $column_data)
 126          {
 127              // here lies an array, filled with information compiled on the column's data
 128              $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
 129  
 130              if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "$column_name}_gen"
 131              {
 132                  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);
 133              }
 134  
 135              // here we add the definition of the new column to the list of columns
 136              $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
 137  
 138              // see if we have found a primary key set due to a column definition if we have found it, we can stop looking
 139              if (!$primary_key_gen)
 140              {
 141                  $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
 142              }
 143  
 144              // create sequence DDL based off of the existance of auto incrementing columns
 145              if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
 146              {
 147                  $create_sequence = $column_name;
 148              }
 149          }
 150  
 151          // this makes up all the columns in the create table statement
 152          $table_sql .= implode(",\n", $columns);
 153  
 154          // we have yet to create a primary key for this table,
 155          // this means that we can add the one we really wanted instead
 156          if (!$primary_key_gen)
 157          {
 158              // Write primary key
 159              if (isset($table_data['PRIMARY_KEY']))
 160              {
 161                  if (!is_array($table_data['PRIMARY_KEY']))
 162                  {
 163                      $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
 164                  }
 165  
 166                  $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
 167              }
 168          }
 169  
 170          // do we need to add a sequence for auto incrementing columns?
 171          if ($create_sequence)
 172          {
 173              $statements[] = "CREATE SEQUENCE {$table_name}_seq;";
 174          }
 175  
 176          // close the table
 177          $table_sql .= "\n);";
 178          $statements[] = $table_sql;
 179  
 180          // Write Keys
 181          if (isset($table_data['KEYS']))
 182          {
 183              foreach ($table_data['KEYS'] as $key_name => $key_data)
 184              {
 185                  if (!is_array($key_data[1]))
 186                  {
 187                      $key_data[1] = array($key_data[1]);
 188                  }
 189  
 190                  $old_return_statements = $this->return_statements;
 191                  $this->return_statements = true;
 192  
 193                  $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]);
 194  
 195                  foreach ($key_stmts as $key_stmt)
 196                  {
 197                      $statements[] = $key_stmt;
 198                  }
 199  
 200                  $this->return_statements = $old_return_statements;
 201              }
 202          }
 203  
 204          // Commit Transaction
 205          $statements[] = 'commit';
 206  
 207          return $this->_sql_run_sql($statements);
 208      }
 209  
 210      /**
 211       * {@inheritDoc}
 212       */
 213  	function sql_list_columns($table_name)
 214      {
 215          $columns = array();
 216  
 217          $sql = "SELECT a.attname
 218              FROM pg_class c, pg_attribute a
 219              WHERE c.relname = '{$table_name}'
 220                  AND a.attnum > 0
 221                  AND a.attrelid = c.oid";
 222          $result = $this->db->sql_query($sql);
 223  
 224          while ($row = $this->db->sql_fetchrow($result))
 225          {
 226              $column = strtolower(current($row));
 227              $columns[$column] = $column;
 228          }
 229          $this->db->sql_freeresult($result);
 230  
 231          return $columns;
 232      }
 233  
 234      /**
 235       * {@inheritDoc}
 236       */
 237  	function sql_index_exists($table_name, $index_name)
 238      {
 239          $sql = "SELECT ic.relname as index_name
 240              FROM pg_class bc, pg_class ic, pg_index i
 241              WHERE (bc.oid = i.indrelid)
 242                  AND (ic.oid = i.indexrelid)
 243                  AND (bc.relname = '" . $table_name . "')
 244                  AND (i.indisunique != 't')
 245                  AND (i.indisprimary != 't')";
 246          $result = $this->db->sql_query($sql);
 247  
 248          while ($row = $this->db->sql_fetchrow($result))
 249          {
 250              // This DBMS prefixes index names with the table name
 251              $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
 252  
 253              if (strtolower($row['index_name']) == strtolower($index_name))
 254              {
 255                  $this->db->sql_freeresult($result);
 256                  return true;
 257              }
 258          }
 259          $this->db->sql_freeresult($result);
 260  
 261          return false;
 262      }
 263  
 264      /**
 265       * {@inheritDoc}
 266       */
 267  	function sql_unique_index_exists($table_name, $index_name)
 268      {
 269          $sql = "SELECT ic.relname as index_name, i.indisunique
 270              FROM pg_class bc, pg_class ic, pg_index i
 271              WHERE (bc.oid = i.indrelid)
 272                  AND (ic.oid = i.indexrelid)
 273                  AND (bc.relname = '" . $table_name . "')
 274                  AND (i.indisprimary != 't')";
 275          $result = $this->db->sql_query($sql);
 276  
 277          while ($row = $this->db->sql_fetchrow($result))
 278          {
 279              if ($row['indisunique'] != 't')
 280              {
 281                  continue;
 282              }
 283  
 284              // This DBMS prefixes index names with the table name
 285              $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
 286  
 287              if (strtolower($row['index_name']) == strtolower($index_name))
 288              {
 289                  $this->db->sql_freeresult($result);
 290                  return true;
 291              }
 292          }
 293          $this->db->sql_freeresult($result);
 294  
 295          return false;
 296      }
 297  
 298      /**
 299      * Function to prepare some column information for better usage
 300      * @access private
 301      */
 302  	function sql_prepare_column_data($table_name, $column_name, $column_data)
 303      {
 304          if (strlen($column_name) > 30)
 305          {
 306              trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
 307          }
 308  
 309          // Get type
 310          list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
 311  
 312          // Adjust default value if db-dependent specified
 313          if (is_array($column_data[1]))
 314          {
 315              $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
 316          }
 317  
 318          $sql = " {$column_type} ";
 319  
 320          $return_array = array(
 321              'column_type'        => $column_type,
 322              'auto_increment'    => false,
 323          );
 324  
 325          if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
 326          {
 327              $default_val = "nextval('{$table_name}_seq')";
 328              $return_array['auto_increment'] = true;
 329          }
 330          else if (!is_null($column_data[1]))
 331          {
 332              $default_val = "'" . $column_data[1] . "'";
 333              $return_array['null'] = 'NOT NULL';
 334              $sql .= 'NOT NULL ';
 335          }
 336          else
 337          {
 338              // Integers need to have 0 instead of empty string as default
 339              if (strpos($column_type, 'INT') === 0)
 340              {
 341                  $default_val = '0';
 342              }
 343              else
 344              {
 345                  $default_val = "'" . $column_data[1] . "'";
 346              }
 347              $return_array['null'] = 'NULL';
 348              $sql .= 'NULL ';
 349          }
 350  
 351          $return_array['default'] = $default_val;
 352  
 353          $sql .= "DEFAULT {$default_val}";
 354  
 355          // Unsigned? Then add a CHECK contraint
 356          if (in_array($orig_column_type, $this->unsigned_types))
 357          {
 358              $return_array['constraint'] = "CHECK ({$column_name} >= 0)";
 359              $sql .= " CHECK ({$column_name} >= 0)";
 360          }
 361  
 362          $return_array['column_type_sql'] = $sql;
 363  
 364          return $return_array;
 365      }
 366  
 367      /**
 368       * {@inheritDoc}
 369       */
 370  	function sql_column_add($table_name, $column_name, $column_data, $inline = false)
 371      {
 372          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
 373          $statements = array();
 374  
 375          // Does not support AFTER, only through temporary table
 376          if (version_compare($this->db->sql_server_info(true), '8.0', '>='))
 377          {
 378              $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql'];
 379          }
 380          else
 381          {
 382              // old versions cannot add columns with default and null information
 383              $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint'];
 384  
 385              if (isset($column_data['null']))
 386              {
 387                  if ($column_data['null'] == 'NOT NULL')
 388                  {
 389                      $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL';
 390                  }
 391              }
 392  
 393              if (isset($column_data['default']))
 394              {
 395                  $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
 396              }
 397          }
 398  
 399          return $this->_sql_run_sql($statements);
 400      }
 401  
 402      /**
 403       * {@inheritDoc}
 404       */
 405  	function sql_column_remove($table_name, $column_name, $inline = false)
 406      {
 407          $statements = array();
 408  
 409          $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
 410  
 411          return $this->_sql_run_sql($statements);
 412      }
 413  
 414      /**
 415       * {@inheritDoc}
 416       */
 417  	function sql_index_drop($table_name, $index_name)
 418      {
 419          $statements = array();
 420  
 421          $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
 422  
 423          return $this->_sql_run_sql($statements);
 424      }
 425  
 426      /**
 427       * {@inheritDoc}
 428       */
 429  	function sql_table_drop($table_name)
 430      {
 431          $statements = array();
 432  
 433          if (!$this->sql_table_exists($table_name))
 434          {
 435              return $this->_sql_run_sql($statements);
 436          }
 437  
 438          // the most basic operation, get rid of the table
 439          $statements[] = 'DROP TABLE ' . $table_name;
 440  
 441          // PGSQL does not "tightly" bind sequences and tables, we must guess...
 442          $sql = "SELECT relname
 443              FROM pg_class
 444              WHERE relkind = 'S'
 445                  AND relname = '{$table_name}_seq'";
 446          $result = $this->db->sql_query($sql);
 447  
 448          // We don't even care about storing the results. We already know the answer if we get rows back.
 449          if ($this->db->sql_fetchrow($result))
 450          {
 451              $statements[] =  "DROP SEQUENCE IF EXISTS {$table_name}_seq;\n";
 452          }
 453          $this->db->sql_freeresult($result);
 454  
 455          return $this->_sql_run_sql($statements);
 456      }
 457  
 458      /**
 459       * {@inheritDoc}
 460       */
 461  	function sql_create_primary_key($table_name, $column, $inline = false)
 462      {
 463          $statements = array();
 464  
 465          $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
 466  
 467          return $this->_sql_run_sql($statements);
 468      }
 469  
 470      /**
 471       * {@inheritDoc}
 472       */
 473  	function sql_create_unique_index($table_name, $index_name, $column)
 474      {
 475          $statements = array();
 476  
 477          $this->check_index_name_length($table_name, $index_name);
 478  
 479          $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
 480  
 481          return $this->_sql_run_sql($statements);
 482      }
 483  
 484      /**
 485       * {@inheritDoc}
 486       */
 487  	function sql_create_index($table_name, $index_name, $column)
 488      {
 489          $statements = array();
 490  
 491          $this->check_index_name_length($table_name, $index_name);
 492  
 493          // remove index length
 494          $column = preg_replace('#:.*$#', '', $column);
 495  
 496          $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
 497  
 498          return $this->_sql_run_sql($statements);
 499      }
 500  
 501  
 502      /**
 503       * {@inheritDoc}
 504       */
 505  	function sql_list_index($table_name)
 506      {
 507          $index_array = array();
 508  
 509          $sql = "SELECT ic.relname as index_name
 510              FROM pg_class bc, pg_class ic, pg_index i
 511              WHERE (bc.oid = i.indrelid)
 512                  AND (ic.oid = i.indexrelid)
 513                  AND (bc.relname = '" . $table_name . "')
 514                  AND (i.indisunique != 't')
 515                  AND (i.indisprimary != 't')";
 516          $result = $this->db->sql_query($sql);
 517  
 518          while ($row = $this->db->sql_fetchrow($result))
 519          {
 520              $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
 521  
 522              $index_array[] = $row['index_name'];
 523          }
 524          $this->db->sql_freeresult($result);
 525  
 526          return array_map('strtolower', $index_array);
 527      }
 528  
 529      /**
 530       * {@inheritDoc}
 531       */
 532  	function sql_column_change($table_name, $column_name, $column_data, $inline = false)
 533      {
 534          $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
 535          $statements = array();
 536  
 537          $sql = 'ALTER TABLE ' . $table_name . ' ';
 538  
 539          $sql_array = array();
 540          $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
 541  
 542          if (isset($column_data['null']))
 543          {
 544              if ($column_data['null'] == 'NOT NULL')
 545              {
 546                  $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
 547              }
 548              else if ($column_data['null'] == 'NULL')
 549              {
 550                  $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
 551              }
 552          }
 553  
 554          if (isset($column_data['default']))
 555          {
 556              $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
 557          }
 558  
 559          // we don't want to double up on constraints if we change different number data types
 560          if (isset($column_data['constraint']))
 561          {
 562              $constraint_sql = "SELECT consrc as constraint_data
 563                  FROM pg_constraint, pg_class bc
 564                  WHERE conrelid = bc.oid
 565                      AND bc.relname = '{$table_name}'
 566                      AND NOT EXISTS (
 567                          SELECT *
 568                          FROM pg_constraint as c, pg_inherits as i
 569                          WHERE i.inhrelid = pg_constraint.conrelid
 570                              AND c.conname = pg_constraint.conname
 571                              AND c.consrc = pg_constraint.consrc
 572                              AND c.conrelid = i.inhparent
 573                      )";
 574  
 575              $constraint_exists = false;
 576  
 577              $result = $this->db->sql_query($constraint_sql);
 578              while ($row = $this->db->sql_fetchrow($result))
 579              {
 580                  if (trim($row['constraint_data']) == trim($column_data['constraint']))
 581                  {
 582                      $constraint_exists = true;
 583                      break;
 584                  }
 585              }
 586              $this->db->sql_freeresult($result);
 587  
 588              if (!$constraint_exists)
 589              {
 590                  $sql_array[] = 'ADD ' . $column_data['constraint'];
 591              }
 592          }
 593  
 594          $sql .= implode(', ', $sql_array);
 595  
 596          $statements[] = $sql;
 597  
 598          return $this->_sql_run_sql($statements);
 599      }
 600  
 601      /**
 602      * Get a list with existing indexes for the column
 603      *
 604      * @param string $table_name
 605      * @param string $column_name
 606      * @param bool $unique Should we get unique indexes or normal ones
 607      * @return array        Array with Index name => columns
 608      */
 609  	public function get_existing_indexes($table_name, $column_name, $unique = false)
 610      {
 611          // Not supported
 612          throw new \Exception('DBMS is not supported');
 613      }
 614  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1