[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |