|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00018 00029 defined('MOODLE_INTERNAL') || die(); 00030 00031 require_once($CFG->libdir.'/ddl/sql_generator.php'); 00032 00036 00037 class postgres_sql_generator extends sql_generator { 00038 00040 00041 public $number_type = 'NUMERIC'; // Proper type for NUMBER(x) in this DB 00042 00043 public $unsigned_allowed = false; // To define in the generator must handle unsigned information 00044 public $default_for_char = ''; // To define the default to set for NOT NULLs CHARs without default (null=do nothing) 00045 00046 public $sequence_extra_code = false; //Does the generator need to add extra code to generate the sequence fields 00047 public $sequence_name = 'BIGSERIAL'; //Particular name for inline sequences in this generator 00048 public $sequence_name_small = 'SERIAL'; //Particular name for inline sequences in this generator 00049 public $sequence_only = true; //To avoid to output the rest of the field specs, leaving only the name and the sequence_name variable 00050 00051 public $enum_inline_code = false; //Does the generator need to add inline code in the column definition 00052 00053 public $rename_index_sql = 'ALTER TABLE OLDINDEXNAME RENAME TO NEWINDEXNAME'; //SQL sentence to rename one index 00054 //TABLENAME, OLDINDEXNAME, NEWINDEXNAME are dynamically replaced 00055 00056 public $rename_key_sql = null; //SQL sentence to rename one key (PostgreSQL doesn't support this!) 00057 //TABLENAME, OLDKEYNAME, NEWKEYNAME are dynamically replaced 00058 00059 protected $std_strings = null; // '' or \' quotes 00060 00066 public function getResetSequenceSQL($table) { 00067 00068 if ($table instanceof xmldb_table) { 00069 $tablename = $table->getName(); 00070 } else { 00071 $tablename = $table; 00072 } 00073 00074 // From http://www.postgresql.org/docs/7.4/static/sql-altersequence.html 00075 $value = (int)$this->mdb->get_field_sql('SELECT MAX(id) FROM {'.$tablename.'}'); 00076 $value++; 00077 return array("ALTER SEQUENCE $this->prefix{$tablename}_id_seq RESTART WITH $value"); 00078 } 00079 00084 public function getCreateTempTableSQL($xmldb_table) { 00085 $this->temptables->add_temptable($xmldb_table->getName()); 00086 $sqlarr = $this->getCreateTableSQL($xmldb_table); 00087 $sqlarr = preg_replace('/^CREATE TABLE/', "CREATE TEMPORARY TABLE", $sqlarr); 00088 return $sqlarr; 00089 } 00090 00095 public function getDropTempTableSQL($xmldb_table) { 00096 $sqlarr = $this->getDropTableSQL($xmldb_table); 00097 $this->temptables->delete_temptable($xmldb_table->getName()); 00098 return $sqlarr; 00099 } 00100 00104 public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) { 00105 00106 switch ($xmldb_type) { 00107 case XMLDB_TYPE_INTEGER: // From http://www.postgresql.org/docs/7.4/interactive/datatype.html 00108 if (empty($xmldb_length)) { 00109 $xmldb_length = 10; 00110 } 00111 if ($xmldb_length > 9) { 00112 $dbtype = 'BIGINT'; 00113 } else if ($xmldb_length > 4) { 00114 $dbtype = 'INTEGER'; 00115 } else { 00116 $dbtype = 'SMALLINT'; 00117 } 00118 break; 00119 case XMLDB_TYPE_NUMBER: 00120 $dbtype = $this->number_type; 00121 if (!empty($xmldb_length)) { 00122 $dbtype .= '(' . $xmldb_length; 00123 if (!empty($xmldb_decimals)) { 00124 $dbtype .= ',' . $xmldb_decimals; 00125 } 00126 $dbtype .= ')'; 00127 } 00128 break; 00129 case XMLDB_TYPE_FLOAT: 00130 $dbtype = 'DOUBLE PRECISION'; 00131 if (!empty($xmldb_decimals)) { 00132 if ($xmldb_decimals < 6) { 00133 $dbtype = 'REAL'; 00134 } 00135 } 00136 break; 00137 case XMLDB_TYPE_CHAR: 00138 $dbtype = 'VARCHAR'; 00139 if (empty($xmldb_length)) { 00140 $xmldb_length='255'; 00141 } 00142 $dbtype .= '(' . $xmldb_length . ')'; 00143 break; 00144 case XMLDB_TYPE_TEXT: 00145 $dbtype = 'TEXT'; 00146 break; 00147 case XMLDB_TYPE_BINARY: 00148 $dbtype = 'BYTEA'; 00149 break; 00150 case XMLDB_TYPE_DATETIME: 00151 $dbtype = 'TIMESTAMP'; 00152 break; 00153 } 00154 return $dbtype; 00155 } 00156 00160 function getCommentSQL ($xmldb_table) { 00161 00162 $comment = "COMMENT ON TABLE " . $this->getTableName($xmldb_table); 00163 $comment.= " IS '" . $this->addslashes(substr($xmldb_table->getComment(), 0, 250)) . "'"; 00164 00165 return array($comment); 00166 } 00167 00171 public function getRenameTableExtraSQL($xmldb_table, $newname) { 00172 00173 $results = array(); 00174 00175 $newt = new xmldb_table($newname); 00176 00177 $xmldb_field = new xmldb_field('id'); // Fields having sequences should be exclusively, id. 00178 00179 $oldseqname = $this->getTableName($xmldb_table) . '_' . $xmldb_field->getName() . '_seq'; 00180 $newseqname = $this->getTableName($newt) . '_' . $xmldb_field->getName() . '_seq'; 00181 00183 $results[] = 'ALTER TABLE ' . $oldseqname . ' RENAME TO ' . $newseqname; 00184 00186 $oldtablename = $this->getTableName($xmldb_table); 00187 $newtablename = $this->getTableName($newt); 00188 00189 $oldconstraintprefix = $this->getNameForObject($xmldb_table->getName(), ''); 00190 $newconstraintprefix = $this->getNameForObject($newt->getName(), '', ''); 00191 00192 if ($constraints = $this->getCheckConstraintsFromDB($xmldb_table)) { 00193 foreach ($constraints as $constraint) { 00195 $results[] = 'ALTER TABLE ' . $newtablename . ' DROP CONSTRAINT ' . $constraint->name; 00196 } 00197 } 00198 00199 return $results; 00200 } 00201 00211 public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) { 00212 $results = array(); 00213 00215 $tablename = $xmldb_table->getName(); 00216 $fieldname = $xmldb_field->getName(); 00217 00219 $meta = $this->mdb->get_columns($tablename); 00220 $metac = $meta[$xmldb_field->getName()]; 00221 $oldmetatype = $metac->meta_type; 00222 $oldlength = $metac->max_length; 00223 $olddecimals = empty($metac->scale) ? null : $metac->scale; 00224 $oldnotnull = empty($metac->not_null) ? false : $metac->not_null; 00225 $olddefault = empty($metac->has_default) ? null : $metac->default_value; 00226 00227 $typechanged = true; //By default, assume that the column type has changed 00228 $precisionchanged = true; //By default, assume that the column precision has changed 00229 $decimalchanged = true; //By default, assume that the column decimal has changed 00230 $defaultchanged = true; //By default, assume that the column default has changed 00231 $notnullchanged = true; //By default, assume that the column notnull has changed 00232 00234 if (($xmldb_field->getType() == XMLDB_TYPE_INTEGER && $oldmetatype == 'I') || 00235 ($xmldb_field->getType() == XMLDB_TYPE_NUMBER && $oldmetatype == 'N') || 00236 ($xmldb_field->getType() == XMLDB_TYPE_FLOAT && $oldmetatype == 'F') || 00237 ($xmldb_field->getType() == XMLDB_TYPE_CHAR && $oldmetatype == 'C') || 00238 ($xmldb_field->getType() == XMLDB_TYPE_TEXT && $oldmetatype == 'X') || 00239 ($xmldb_field->getType() == XMLDB_TYPE_BINARY && $oldmetatype == 'B')) { 00240 $typechanged = false; 00241 } 00243 if (($xmldb_field->getType() == XMLDB_TYPE_TEXT) || 00244 ($xmldb_field->getType() == XMLDB_TYPE_BINARY) || 00245 ($oldlength == -1) || 00246 ($xmldb_field->getLength() == $oldlength)) { 00247 $precisionchanged = false; 00248 } 00250 if (($xmldb_field->getType() == XMLDB_TYPE_INTEGER) || 00251 ($xmldb_field->getType() == XMLDB_TYPE_CHAR) || 00252 ($xmldb_field->getType() == XMLDB_TYPE_TEXT) || 00253 ($xmldb_field->getType() == XMLDB_TYPE_BINARY) || 00254 (!$xmldb_field->getDecimals()) || 00255 (!$olddecimals) || 00256 ($xmldb_field->getDecimals() == $olddecimals)) { 00257 $decimalchanged = false; 00258 } 00260 if (($xmldb_field->getDefault() === null && $olddefault === null) || 00261 ($xmldb_field->getDefault() === $olddefault)) { 00262 $defaultchanged = false; 00263 } 00265 if (($xmldb_field->getNotnull() === $oldnotnull)) { 00266 $notnullchanged = false; 00267 } 00268 00270 $tablename = $this->getTableName($xmldb_table); 00271 $fieldname = $this->getEncQuoted($xmldb_field->getName()); 00272 00274 $specschanged = $typechanged || $precisionchanged || $decimalchanged; 00275 00277 if ($specschanged) { 00279 if ($olddefault !== null) { 00280 $results[] = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' DROP DEFAULT'; 00281 } 00282 $alterstmt = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $this->getEncQuoted($xmldb_field->getName()) . 00283 ' TYPE' . $this->getFieldSQL($xmldb_table, $xmldb_field, null, true, true, null, false); 00285 if (($oldmetatype == 'C' || $oldmetatype == 'X') && 00286 ($xmldb_field->getType() == XMLDB_TYPE_NUMBER || $xmldb_field->getType() == XMLDB_TYPE_FLOAT)) { 00287 $alterstmt .= ' USING CAST('.$fieldname.' AS NUMERIC)'; // from char or text to number or float 00288 } else if (($oldmetatype == 'C' || $oldmetatype == 'X') && 00289 $xmldb_field->getType() == XMLDB_TYPE_INTEGER) { 00290 $alterstmt .= ' USING CAST(CAST('.$fieldname.' AS NUMERIC) AS INTEGER)'; // From char to integer 00291 } 00292 $results[] = $alterstmt; 00293 } 00294 00296 if ($defaultchanged || $specschanged) { 00297 $default_clause = $this->getDefaultClause($xmldb_field); 00298 if ($default_clause) { 00299 $sql = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' SET' . $default_clause; 00300 $results[] = $sql; 00301 } else { 00302 if (!$specschanged) { 00303 $results[] = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' DROP DEFAULT'; 00304 } 00305 } 00306 } 00307 00309 if ($notnullchanged) { 00310 if ($xmldb_field->getNotnull()) { 00311 $results[] = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' SET NOT NULL'; 00312 } else { 00313 $results[] = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' DROP NOT NULL'; 00314 } 00315 } 00316 00318 return $results; 00319 } 00320 00327 public function getDropEnumSQL($xmldb_table, $xmldb_field) { 00329 if ($check_constraints = $this->getCheckConstraintsFromDB($xmldb_table, $xmldb_field)) { 00330 $check_constraint = array_shift($check_constraints); 00331 $constraint_name = strtolower($check_constraint->name); 00332 00333 return array('ALTER TABLE ' . $this->getTableName($xmldb_table) . 00334 ' DROP CONSTRAINT ' . $constraint_name); 00335 } else { 00336 return array(); 00337 } 00338 } 00339 00344 public function getCreateDefaultSQL($xmldb_table, $xmldb_field) { 00347 return $this->getAlterFieldSQL($xmldb_table, $xmldb_field); 00348 } 00349 00354 public function getDropDefaultSQL($xmldb_table, $xmldb_field) { 00357 return $this->getAlterFieldSQL($xmldb_table, $xmldb_field); 00358 } 00359 00370 public function getCheckConstraintsFromDB($xmldb_table, $xmldb_field = null) { 00371 00372 $results = array(); 00373 00374 $tablename = $this->getTableName($xmldb_table); 00375 00376 if ($constraints = $this->mdb->get_records_sql("SELECT co.conname AS name, co.consrc AS description 00377 FROM pg_constraint co, pg_class cl 00378 WHERE co.conrelid = cl.oid 00379 AND co.contype = 'c' AND cl.relname = ?", 00380 array($tablename))) { 00381 foreach ($constraints as $constraint) { 00382 $results[$constraint->name] = $constraint; 00383 } 00384 } 00385 00387 if ($xmldb_field) { 00388 $filtered_results = array(); 00389 $filter = $xmldb_field->getName(); 00391 foreach ($results as $key => $result) { 00392 $description = preg_replace('/\("(.*?)"\)/', '($1)', $result->description);// Double quotes out 00393 $description = preg_replace('/[\(\)]/', '', $description); // Parenthesis out 00394 $description = preg_replace('/::[a-z]+/i', '', $description); // Casts out 00395 $description = preg_replace("/({$filter})/i", '@$1@', $description); 00396 $description = trim(preg_replace('/ or /i', ' OR ', $description)); // Uppercase or & trim 00398 if (preg_match("/^@{$filter}@/i", $description)) { 00399 $filtered_results[$key] = $result; 00400 } 00401 } 00403 $results = $filtered_results; 00404 } 00405 00406 return $results; 00407 } 00408 00409 public function addslashes($s) { 00410 // Postgres is gradually switching to ANSI quotes, we need to check what is expected 00411 if (!isset($this->std_strings)) { 00412 $this->std_strings = ($this->mdb->get_field_sql("select setting from pg_settings where name = 'standard_conforming_strings'") === 'on'); 00413 } 00414 00415 if ($this->std_strings) { 00416 $s = str_replace("'", "''", $s); 00417 } else { 00418 // do not use php addslashes() because it depends on PHP quote settings! 00419 $s = str_replace('\\','\\\\',$s); 00420 $s = str_replace("\0","\\\0", $s); 00421 $s = str_replace("'", "\\'", $s); 00422 } 00423 00424 return $s; 00425 } 00426 00435 function getSequenceFromDB($xmldb_table) { 00436 00437 $tablename = $this->getTableName($xmldb_table); 00438 $sequencename = $tablename . '_id_seq'; 00439 00440 if (!$this->mdb->get_record_sql("SELECT * 00441 FROM pg_class 00442 WHERE relname = ? AND relkind = 'S'", 00443 array($sequencename))) { 00444 $sequencename = false; 00445 } 00446 00447 return $sequencename; 00448 } 00449 00455 public function isNameInUse($object_name, $type, $table_name) { 00456 switch($type) { 00457 case 'ix': 00458 case 'uix': 00459 case 'seq': 00460 if ($check = $this->mdb->get_records_sql("SELECT relname 00461 FROM pg_class 00462 WHERE lower(relname) = ?", array(strtolower($object_name)))) { 00463 return true; 00464 } 00465 break; 00466 case 'pk': 00467 case 'uk': 00468 case 'fk': 00469 case 'ck': 00470 if ($check = $this->mdb->get_records_sql("SELECT conname 00471 FROM pg_constraint 00472 WHERE lower(conname) = ?", array(strtolower($object_name)))) { 00473 return true; 00474 } 00475 break; 00476 case 'trg': 00477 if ($check = $this->mdb->get_records_sql("SELECT tgname 00478 FROM pg_trigger 00479 WHERE lower(tgname) = ?", array(strtolower($object_name)))) { 00480 return true; 00481 } 00482 break; 00483 } 00484 return false; //No name in use found 00485 } 00486 00490 public static function getReservedWords() { 00493 $reserved_words = array ( 00494 'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc', 00495 'asymmetric', 'authorization', 'between', 'binary', 'both', 'case', 00496 'cast', 'check', 'collate', 'column', 'constraint', 'create', 'cross', 00497 'current_date', 'current_role', 'current_time', 'current_timestamp', 00498 'current_user', 'default', 'deferrable', 'desc', 'distinct', 'do', 00499 'else', 'end', 'except', 'false', 'for', 'foreign', 'freeze', 'from', 00500 'full', 'grant', 'group', 'having', 'ilike', 'in', 'initially', 'inner', 00501 'intersect', 'into', 'is', 'isnull', 'join', 'leading', 'left', 'like', 00502 'limit', 'localtime', 'localtimestamp', 'natural', 'new', 'not', 00503 'notnull', 'null', 'off', 'offset', 'old', 'on', 'only', 'or', 'order', 00504 'outer', 'overlaps', 'placing', 'primary', 'references', 'returning', 'right', 'select', 00505 'session_user', 'similar', 'some', 'symmetric', 'table', 'then', 'to', 00506 'trailing', 'true', 'union', 'unique', 'user', 'using', 'verbose', 00507 'when', 'where', 'with' 00508 ); 00509 return $reserved_words; 00510 } 00511 }