[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Schema/ChangeItem/ -> PostgresqlChangeItem.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Schema\ChangeItem;
  11  
  12  use Joomla\CMS\Schema\ChangeItem;
  13  
  14  // phpcs:disable PSR1.Files.SideEffects
  15  \defined('JPATH_PLATFORM') or die;
  16  // phpcs:enable PSR1.Files.SideEffects
  17  
  18  /**
  19   * Checks the database schema against one PostgreSQL DDL query to see if it has been run.
  20   *
  21   * @since  3.0
  22   */
  23  class PostgresqlChangeItem extends ChangeItem
  24  {
  25      /**
  26       * Checks a DDL query to see if it is a known type
  27       * If yes, build a check query to see if the DDL has been run on the database.
  28       * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated.
  29       * The $msgElements contains the text to create the user message.
  30       * The $checkQuery contains the SQL query to check whether the schema change has
  31       * been run against the current database. The $queryType contains the type of
  32       * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX).
  33       * The $checkStatus field is set to zero if the query is created
  34       *
  35       * If not successful, $checkQuery is empty and , and $checkStatus is -1.
  36       * For example, this will happen if the current line is a non-DDL statement.
  37       *
  38       * @return void
  39       *
  40       * @since  3.0
  41       */
  42      protected function buildCheckQuery()
  43      {
  44          // Initialize fields in case we can't create a check query
  45  
  46          // Change status to skipped
  47          $this->checkStatus = -1;
  48  
  49          $result = null;
  50          $splitIntoWords = "~'[^']*'(*SKIP)(*F)|\s+~";
  51          $splitIntoActions = "~'[^']*'(*SKIP)(*F)|\([^)]*\)(*SKIP)(*F)|,~";
  52  
  53          // Remove any newlines
  54          $this->updateQuery = str_replace("\n", '', $this->updateQuery);
  55  
  56          // Remove trailing whitespace and semicolon
  57          $this->updateQuery = rtrim($this->updateQuery, "; \t\n\r\0\x0B");
  58  
  59          // Fix up extra spaces around () and in general
  60          $find = array('#((\s*)\(\s*([^)\s]+)\s*)(\))#', '#(\s)(\s*)#');
  61          $replace = array('($3)', '$1');
  62          $updateQuery = preg_replace($find, $replace, $this->updateQuery);
  63          $wordArray = preg_split($splitIntoWords, $updateQuery, -1, PREG_SPLIT_NO_EMPTY);
  64  
  65          $totalWords = \count($wordArray);
  66  
  67          // First, make sure we have an array of at least 6 elements
  68          // if not, we can't make a check query for this one
  69          if ($totalWords < 6) {
  70              // Done with method
  71              return;
  72          }
  73  
  74          // We can only make check queries for alter table and create table queries
  75          $command = strtoupper($wordArray[0] . ' ' . $wordArray[1]);
  76  
  77          if ($command === 'ALTER TABLE') {
  78              // Check only the last action
  79              $actions = ltrim(substr($updateQuery, strpos($updateQuery, $wordArray[2]) + \strlen($wordArray[2])));
  80              $actions = preg_split($splitIntoActions, $actions);
  81  
  82              // Get the last action
  83              $lastActionArray = preg_split($splitIntoWords, end($actions), -1, PREG_SPLIT_NO_EMPTY);
  84  
  85              // Replace all actions by the last one
  86              array_splice($wordArray, 3, $totalWords, $lastActionArray);
  87  
  88              $alterCommand = strtoupper($wordArray[3] . ' ' . $wordArray[4]);
  89  
  90              if ($alterCommand === 'RENAME TO') {
  91                  $table = $this->fixQuote($wordArray[5]);
  92                  $result = 'SELECT table_name FROM information_schema.tables WHERE table_name=' . $table;
  93                  $this->queryType = 'RENAME_TABLE';
  94                  $this->checkQueryExpected = 1;
  95                  $this->msgElements = array($table);
  96              } elseif ($alterCommand === 'ADD COLUMN') {
  97                  $result = 'SELECT column_name'
  98                      . ' FROM information_schema.columns'
  99                      . ' WHERE table_name='
 100                      . $this->fixQuote($wordArray[2])
 101                      . ' AND column_name=' . $this->fixQuote($wordArray[5]);
 102  
 103                  $this->queryType = 'ADD_COLUMN';
 104                  $this->msgElements = array(
 105                      $this->fixQuote($wordArray[2]),
 106                      $this->fixQuote($wordArray[5])
 107                  );
 108              } elseif ($alterCommand === 'DROP COLUMN') {
 109                  $result = 'SELECT column_name'
 110                      . ' FROM information_schema.columns'
 111                      . ' WHERE table_name='
 112                      . $this->fixQuote($wordArray[2])
 113                      . ' AND column_name=' . $this->fixQuote($wordArray[5]);
 114  
 115                  $this->queryType = 'DROP_COLUMN';
 116                  $this->checkQueryExpected = 0;
 117                  $this->msgElements = array(
 118                      $this->fixQuote($wordArray[2]),
 119                      $this->fixQuote($wordArray[5])
 120                  );
 121              } elseif ($alterCommand === 'ALTER COLUMN') {
 122                  $alterAction = strtoupper($wordArray[6]);
 123  
 124                  if ($alterAction === 'TYPE') {
 125                      $type = implode(' ', \array_slice($wordArray, 7));
 126  
 127                      if ($pos = stripos($type, ' USING ')) {
 128                          $type = substr($type, 0, $pos);
 129                      }
 130  
 131                      if ($pos = strpos($type, '(')) {
 132                          $datatype = substr($type, 0, $pos);
 133                      } else {
 134                          $datatype = $type;
 135                      }
 136  
 137                      if ($datatype === 'varchar') {
 138                          $datatype = 'character varying';
 139                      }
 140  
 141                      $result = 'SELECT column_name, data_type '
 142                          . 'FROM information_schema.columns WHERE table_name='
 143                          . $this->fixQuote($wordArray[2]) . ' AND column_name='
 144                          . $this->fixQuote($wordArray[5])
 145                          . ' AND data_type=' . $this->fixQuote($datatype);
 146  
 147                      if ($datatype === 'character varying') {
 148                          $result .= ' AND character_maximum_length = ' . (int) substr($type, $pos + 1);
 149                      }
 150  
 151                      $this->queryType = 'CHANGE_COLUMN_TYPE';
 152                      $this->msgElements = array(
 153                          $this->fixQuote($wordArray[2]),
 154                          $this->fixQuote($wordArray[5]),
 155                          $type
 156                      );
 157                  } elseif ($alterAction === 'SET') {
 158                      $alterType = strtoupper($wordArray[7]);
 159  
 160                      if ($alterType === 'NOT' && strtoupper($wordArray[8]) === 'NULL') {
 161                          $result = 'SELECT column_name, data_type, is_nullable'
 162                              . ' FROM information_schema.columns'
 163                              . ' WHERE table_name=' . $this->fixQuote($wordArray[2])
 164                              . ' AND column_name=' . $this->fixQuote($wordArray[5])
 165                              . ' AND is_nullable=' . $this->fixQuote('NO');
 166  
 167                          $this->queryType = 'CHANGE_COLUMN_TYPE';
 168                          $this->msgElements = array(
 169                              $this->fixQuote($wordArray[2]),
 170                              $this->fixQuote($wordArray[5]),
 171                              'NOT NULL'
 172                          );
 173                      } elseif ($alterType === 'DEFAULT') {
 174                          $result = 'SELECT column_name, data_type, is_nullable'
 175                              . ' FROM information_schema.columns'
 176                              . ' WHERE table_name=' . $this->fixQuote($wordArray[2])
 177                              . ' AND column_name=' . $this->fixQuote($wordArray[5])
 178                              . ' AND (CASE (position(' . $this->db->quote('::') . ' in column_default))'
 179                              . ' WHEN 0 THEN '
 180                              . ' column_default = ' . $this->db->quote($wordArray[8])
 181                              . ' ELSE '
 182                              . ' substring(column_default, 1, (position(' . $this->db->quote('::')
 183                              . ' in column_default) -1))  = ' . $this->db->quote($wordArray[8])
 184                              . ' END)';
 185  
 186                          $this->queryType = 'CHANGE_COLUMN_TYPE';
 187                          $this->msgElements = array(
 188                              $this->fixQuote($wordArray[2]),
 189                              $this->fixQuote($wordArray[5]),
 190                              'DEFAULT ' . $wordArray[8]
 191                          );
 192                      }
 193                  } elseif ($alterAction === 'DROP') {
 194                      $alterType = strtoupper($wordArray[7]);
 195  
 196                      if ($alterType === 'DEFAULT') {
 197                          $result = 'SELECT column_name, data_type, is_nullable , column_default'
 198                              . ' FROM information_schema.columns'
 199                              . ' WHERE table_name=' . $this->fixQuote($wordArray[2])
 200                              . ' AND column_name=' . $this->fixQuote($wordArray[5])
 201                              . ' AND column_default IS NOT NULL';
 202  
 203                          $this->queryType = 'CHANGE_COLUMN_TYPE';
 204                          $this->checkQueryExpected = 0;
 205                          $this->msgElements = array(
 206                              $this->fixQuote($wordArray[2]),
 207                              $this->fixQuote($wordArray[5]),
 208                              'NOT DEFAULT'
 209                          );
 210                      } elseif ($alterType === 'NOT' && strtoupper($wordArray[8]) === 'NULL') {
 211                          $result = 'SELECT column_name, data_type, is_nullable , column_default'
 212                              . ' FROM information_schema.columns'
 213                              . ' WHERE table_name=' . $this->fixQuote($wordArray[2])
 214                              . ' AND column_name=' . $this->fixQuote($wordArray[5])
 215                              . ' AND is_nullable = ' . $this->fixQuote('NO');
 216  
 217                          $this->queryType = 'CHANGE_COLUMN_TYPE';
 218                          $this->checkQueryExpected = 0;
 219                          $this->msgElements = array(
 220                              $this->fixQuote($wordArray[2]),
 221                              $this->fixQuote($wordArray[5]),
 222                              'NULL'
 223                          );
 224                      }
 225                  }
 226              }
 227          } elseif ($command === 'DROP INDEX') {
 228              if (strtoupper($wordArray[2] . $wordArray[3]) === 'IFEXISTS') {
 229                  $idx = $this->fixQuote($wordArray[4]);
 230              } else {
 231                  $idx = $this->fixQuote($wordArray[2]);
 232              }
 233  
 234              $result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx;
 235              $this->queryType = 'DROP_INDEX';
 236              $this->checkQueryExpected = 0;
 237              $this->msgElements = array($this->fixQuote($idx));
 238          } elseif ($command === 'CREATE INDEX' || (strtoupper($command . $wordArray[2]) === 'CREATE UNIQUE INDEX')) {
 239              if ($wordArray[1] === 'UNIQUE') {
 240                  $idx = $this->fixQuote($wordArray[3]);
 241                  $table = $this->fixQuote($wordArray[5]);
 242              } else {
 243                  $idx = $this->fixQuote($wordArray[2]);
 244                  $table = $this->fixQuote($wordArray[4]);
 245              }
 246  
 247              $result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx . ' AND tablename=' . $table;
 248              $this->queryType = 'ADD_INDEX';
 249              $this->checkQueryExpected = 1;
 250              $this->msgElements = array($table, $idx);
 251          }
 252  
 253          if ($command === 'CREATE TABLE') {
 254              if (strtoupper($wordArray[2] . $wordArray[3] . $wordArray[4]) === 'IFNOTEXISTS') {
 255                  $table = $this->fixQuote($wordArray[5]);
 256              } else {
 257                  $table = $this->fixQuote($wordArray[2]);
 258              }
 259  
 260              $result = 'SELECT table_name FROM information_schema.tables WHERE table_name=' . $table;
 261              $this->queryType = 'CREATE_TABLE';
 262              $this->checkQueryExpected = 1;
 263              $this->msgElements = array($table);
 264          }
 265  
 266          // Set fields based on results
 267          if ($this->checkQuery = $result) {
 268              // Unchecked status
 269              $this->checkStatus = 0;
 270          } else {
 271              // Skipped
 272              $this->checkStatus = -1;
 273          }
 274      }
 275  
 276      /**
 277       * Fix up integer. Fixes problem with PostgreSQL integer descriptions.
 278       * If you change a column to "integer unsigned" it shows
 279       * as "int(10) unsigned" in the check query.
 280       *
 281       * @param   string  $type1  the column type
 282       * @param   string  $type2  the column attributes
 283       *
 284       * @return  string  The original or changed column type.
 285       *
 286       * @since   3.0
 287       */
 288      private function fixInteger($type1, $type2)
 289      {
 290          $result = $type1;
 291  
 292          if (strtolower($type1) === 'integer' && strtolower(substr($type2, 0, 8)) === 'unsigned') {
 293              $result = 'unsigned int(10)';
 294          }
 295  
 296          return $result;
 297      }
 298  
 299      /**
 300       * Fixes up a string for inclusion in a query.
 301       * Replaces name quote character with normal quote for literal.
 302       * Drops trailing semicolon. Injects the database prefix.
 303       *
 304       * @param   string  $string  The input string to be cleaned up.
 305       *
 306       * @return  string  The modified string.
 307       *
 308       * @since   3.0
 309       */
 310      private function fixQuote($string)
 311      {
 312          $string = str_replace('"', '', $string);
 313          $string = str_replace(';', '', $string);
 314          $string = str_replace('#__', $this->db->getPrefix(), $string);
 315  
 316          return $this->db->quote($string);
 317      }
 318  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer