PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/ddl/oracle_sql_generator.php

https://bitbucket.org/kudutest1/moodlegit
PHP | 706 lines | 368 code | 85 blank | 253 comment | 94 complexity | 1da9eb35fc19499f6a573c0964780e3e MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Oracle specific SQL code generator.
  18. *
  19. * @package core_ddl
  20. * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
  21. * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. require_once($CFG->libdir.'/ddl/sql_generator.php');
  26. /**
  27. * This class generate SQL code to be used against Oracle
  28. * It extends XMLDBgenerator so everything can be
  29. * overridden as needed to generate correct SQL.
  30. *
  31. * @package core_ddl
  32. * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
  33. * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  34. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35. */
  36. class oracle_sql_generator extends sql_generator {
  37. // Only set values that are different from the defaults present in XMLDBgenerator
  38. /**
  39. * @var string To be automatically added at the end of each statement.
  40. * note: Using "/" because the standard ";" isn't good for stored procedures (triggers)
  41. */
  42. public $statement_end = "\n/";
  43. /** @var string Proper type for NUMBER(x) in this DB. */
  44. public $number_type = 'NUMBER';
  45. /**
  46. * @var string To define the default to set for NOT NULLs CHARs without default (null=do nothing).
  47. * note: Using this whitespace here because Oracle doesn't distinguish empty and null! :-(
  48. */
  49. public $default_for_char = ' ';
  50. /** @var bool To specify if the generator must use some DEFAULT clause to drop defaults.*/
  51. public $drop_default_value_required = true;
  52. /** @var string The DEFAULT clause required to drop defaults.*/
  53. public $drop_default_value = null;
  54. /** @var bool To decide if the default clause of each field must go after the null clause.*/
  55. public $default_after_null = false;
  56. /** @var bool True if the generator needs to add extra code to generate the sequence fields.*/
  57. public $sequence_extra_code = true;
  58. /** @var string The particular name for inline sequences in this generator.*/
  59. public $sequence_name = '';
  60. /** @var string The SQL template to alter columns where the 'TABLENAME' and 'COLUMNSPECS' keywords are dynamically replaced.*/
  61. public $alter_column_sql = 'ALTER TABLE TABLENAME MODIFY (COLUMNSPECS)';
  62. /** @var int var ugly Oracle hack - size of the sequences values cache (20 = Default)*/
  63. public $sequence_cache_size = 20;
  64. /**
  65. * Reset a sequence to the id field of a table.
  66. *
  67. * @param xmldb_table|string $table name of table or the table object.
  68. * @return array of sql statements
  69. */
  70. public function getResetSequenceSQL($table) {
  71. if (is_string($table)) {
  72. $tablename = $table;
  73. $xmldb_table = new xmldb_table($tablename);
  74. } else {
  75. $tablename = $table->getName();
  76. $xmldb_table = $table;
  77. }
  78. // From http://www.acs.ilstu.edu/docs/oracle/server.101/b10759/statements_2011.htm
  79. $value = (int)$this->mdb->get_field_sql('SELECT MAX(id) FROM {'.$tablename.'}');
  80. $value++;
  81. $seqname = $this->getSequenceFromDB($xmldb_table);
  82. if (!$seqname) {
  83. // Fallback, seqname not found, something is wrong. Inform and use the alternative getNameForObject() method
  84. $seqname = $this->getNameForObject($table, 'id', 'seq');
  85. }
  86. return array ("DROP SEQUENCE $seqname",
  87. "CREATE SEQUENCE $seqname START WITH $value INCREMENT BY 1 NOMAXVALUE CACHE $this->sequence_cache_size");
  88. }
  89. /**
  90. * Given one xmldb_table, returns it's correct name, depending of all the parametrization
  91. * Overridden to allow change of names in temp tables
  92. *
  93. * @param xmldb_table table whose name we want
  94. * @param boolean to specify if the name must be quoted (if reserved word, only!)
  95. * @return string the correct name of the table
  96. */
  97. public function getTableName(xmldb_table $xmldb_table, $quoted=true) {
  98. // Get the name, supporting special oci names for temp tables
  99. if ($this->temptables->is_temptable($xmldb_table->getName())) {
  100. $tablename = $this->temptables->get_correct_name($xmldb_table->getName());
  101. } else {
  102. $tablename = $this->prefix . $xmldb_table->getName();
  103. }
  104. // Apply quotes optionally
  105. if ($quoted) {
  106. $tablename = $this->getEncQuoted($tablename);
  107. }
  108. return $tablename;
  109. }
  110. /**
  111. * Given one correct xmldb_table, returns the SQL statements
  112. * to create temporary table (inside one array).
  113. *
  114. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  115. * @return array of sql statements
  116. */
  117. public function getCreateTempTableSQL($xmldb_table) {
  118. $this->temptables->add_temptable($xmldb_table->getName());
  119. $sqlarr = $this->getCreateTableSQL($xmldb_table);
  120. $sqlarr = preg_replace('/^CREATE TABLE (.*)/s', 'CREATE GLOBAL TEMPORARY TABLE $1 ON COMMIT PRESERVE ROWS', $sqlarr);
  121. return $sqlarr;
  122. }
  123. /**
  124. * Given one correct xmldb_table, returns the SQL statements
  125. * to drop it (inside one array).
  126. *
  127. * @param xmldb_table $xmldb_table The table to drop.
  128. * @return array SQL statement(s) for dropping the specified table.
  129. */
  130. public function getDropTableSQL($xmldb_table) {
  131. $sqlarr = parent::getDropTableSQL($xmldb_table);
  132. if ($this->temptables->is_temptable($xmldb_table->getName())) {
  133. array_unshift($sqlarr, "TRUNCATE TABLE ". $this->getTableName($xmldb_table)); // oracle requires truncate before being able to drop a temp table
  134. $this->temptables->delete_temptable($xmldb_table->getName());
  135. }
  136. return $sqlarr;
  137. }
  138. /**
  139. * Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
  140. *
  141. * @param int $xmldb_type The xmldb_type defined constant. XMLDB_TYPE_INTEGER and other XMLDB_TYPE_* constants.
  142. * @param int $xmldb_length The length of that data type.
  143. * @param int $xmldb_decimals The decimal places of precision of the data type.
  144. * @return string The DB defined data type.
  145. */
  146. public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
  147. switch ($xmldb_type) {
  148. case XMLDB_TYPE_INTEGER: // See http://www.acs.ilstu.edu/docs/oracle/server.101/b10759/sql_elements001.htm#sthref86.
  149. if (empty($xmldb_length)) {
  150. $xmldb_length = 10;
  151. }
  152. $dbtype = 'NUMBER(' . $xmldb_length . ')';
  153. break;
  154. case XMLDB_TYPE_FLOAT:
  155. case XMLDB_TYPE_NUMBER:
  156. $dbtype = $this->number_type;
  157. // 38 is the max allowed
  158. if ($xmldb_length > 38) {
  159. $xmldb_length = 38;
  160. }
  161. if (!empty($xmldb_length)) {
  162. $dbtype .= '(' . $xmldb_length;
  163. if (!empty($xmldb_decimals)) {
  164. $dbtype .= ',' . $xmldb_decimals;
  165. }
  166. $dbtype .= ')';
  167. }
  168. break;
  169. case XMLDB_TYPE_CHAR:
  170. // Do not use NVARCHAR2 here because it has hardcoded 1333 char limit,
  171. // VARCHAR2 allows us to create larger fields that error out later during runtime
  172. // only when too many non-ascii utf-8 chars present.
  173. $dbtype = 'VARCHAR2';
  174. if (empty($xmldb_length)) {
  175. $xmldb_length='255';
  176. }
  177. $dbtype .= '(' . $xmldb_length . ' CHAR)'; // CHAR is required because BYTE is the default
  178. break;
  179. case XMLDB_TYPE_TEXT:
  180. $dbtype = 'CLOB';
  181. break;
  182. case XMLDB_TYPE_BINARY:
  183. $dbtype = 'BLOB';
  184. break;
  185. case XMLDB_TYPE_DATETIME:
  186. $dbtype = 'DATE';
  187. break;
  188. }
  189. return $dbtype;
  190. }
  191. /**
  192. * Returns the code (array of statements) needed
  193. * to create one sequence for the xmldb_table and xmldb_field passed in.
  194. *
  195. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  196. * @param xmldb_field $xmldb_field The xmldb_field object instance.
  197. * @return array Array of SQL statements to create the sequence.
  198. */
  199. public function getCreateSequenceSQL($xmldb_table, $xmldb_field) {
  200. $results = array();
  201. $sequence_name = $this->getNameForObject($xmldb_table->getName(), $xmldb_field->getName(), 'seq');
  202. $sequence = "CREATE SEQUENCE $sequence_name START WITH 1 INCREMENT BY 1 NOMAXVALUE CACHE $this->sequence_cache_size";
  203. $results[] = $sequence;
  204. $results = array_merge($results, $this->getCreateTriggerSQL ($xmldb_table, $xmldb_field, $sequence_name));
  205. return $results;
  206. }
  207. /**
  208. * Returns the code needed to create one trigger for the xmldb_table and xmldb_field passed
  209. *
  210. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  211. * @param xmldb_field $xmldb_field The xmldb_field object instance.
  212. * @param string $sequence_name
  213. * @return array Array of SQL statements to create the sequence.
  214. */
  215. public function getCreateTriggerSQL($xmldb_table, $xmldb_field, $sequence_name) {
  216. $trigger_name = $this->getNameForObject($xmldb_table->getName(), $xmldb_field->getName(), 'trg');
  217. $trigger = "CREATE TRIGGER " . $trigger_name;
  218. $trigger.= "\n BEFORE INSERT";
  219. $trigger.= "\nON " . $this->getTableName($xmldb_table);
  220. $trigger.= "\n FOR EACH ROW";
  221. $trigger.= "\nBEGIN";
  222. $trigger.= "\n IF :new." . $this->getEncQuoted($xmldb_field->getName()) . ' IS NULL THEN';
  223. $trigger.= "\n SELECT " . $sequence_name . '.nextval INTO :new.' . $this->getEncQuoted($xmldb_field->getName()) . " FROM dual;";
  224. $trigger.= "\n END IF;";
  225. $trigger.= "\nEND;";
  226. return array($trigger);
  227. }
  228. /**
  229. * Returns the code needed to drop one sequence for the xmldb_table and xmldb_field passed
  230. * Can, optionally, specify if the underlying trigger will be also dropped
  231. *
  232. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  233. * @param xmldb_field $xmldb_field The xmldb_field object instance.
  234. * @param bool $include_trigger
  235. * @return array Array of SQL statements to create the sequence.
  236. */
  237. public function getDropSequenceSQL($xmldb_table, $xmldb_field, $include_trigger=false) {
  238. $result = array();
  239. if ($sequence_name = $this->getSequenceFromDB($xmldb_table)) {
  240. $result[] = "DROP SEQUENCE " . $sequence_name;
  241. }
  242. if ($trigger_name = $this->getTriggerFromDB($xmldb_table) && $include_trigger) {
  243. $result[] = "DROP TRIGGER " . $trigger_name;
  244. }
  245. return $result;
  246. }
  247. /**
  248. * Returns the code (array of statements) needed to add one comment to the table.
  249. *
  250. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  251. * @return array Array of SQL statements to add one comment to the table.
  252. */
  253. function getCommentSQL ($xmldb_table) {
  254. $comment = "COMMENT ON TABLE " . $this->getTableName($xmldb_table);
  255. $comment.= " IS '" . $this->addslashes(substr($xmldb_table->getComment(), 0, 250)) . "'";
  256. return array($comment);
  257. }
  258. /**
  259. * Returns the code (array of statements) needed to execute extra statements on table drop
  260. *
  261. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  262. * @return array Array of extra SQL statements to drop a table.
  263. */
  264. public function getDropTableExtraSQL($xmldb_table) {
  265. $xmldb_field = new xmldb_field('id'); // Fields having sequences should be exclusively, id.
  266. return $this->getDropSequenceSQL($xmldb_table, $xmldb_field, false);
  267. }
  268. /**
  269. * Returns the code (array of statements) needed to execute extra statements on table rename.
  270. *
  271. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  272. * @param string $newname The new name for the table.
  273. * @return array Array of extra SQL statements to rename a table.
  274. */
  275. public function getRenameTableExtraSQL($xmldb_table, $newname) {
  276. $results = array();
  277. $xmldb_field = new xmldb_field('id'); // Fields having sequences should be exclusively, id.
  278. $oldseqname = $this->getSequenceFromDB($xmldb_table);
  279. $newseqname = $this->getNameForObject($newname, $xmldb_field->getName(), 'seq');
  280. $oldtriggername = $this->getTriggerFromDB($xmldb_table);
  281. $newtriggername = $this->getNameForObject($newname, $xmldb_field->getName(), 'trg');
  282. // Drop old trigger (first of all)
  283. $results[] = "DROP TRIGGER " . $oldtriggername;
  284. // Rename the sequence, disablig CACHE before and enablig it later
  285. // to avoid consuming of values on rename
  286. $results[] = 'ALTER SEQUENCE ' . $oldseqname . ' NOCACHE';
  287. $results[] = 'RENAME ' . $oldseqname . ' TO ' . $newseqname;
  288. $results[] = 'ALTER SEQUENCE ' . $newseqname . ' CACHE ' . $this->sequence_cache_size;
  289. // Create new trigger
  290. $newt = new xmldb_table($newname); // Temp table for trigger code generation
  291. $results = array_merge($results, $this->getCreateTriggerSQL($newt, $xmldb_field, $newseqname));
  292. return $results;
  293. }
  294. /**
  295. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to alter the field in the table.
  296. *
  297. * Oracle has some severe limits:
  298. * - clob and blob fields doesn't allow type to be specified
  299. * - error is dropped if the null/not null clause is specified and hasn't changed
  300. * - changes in precision/decimals of numeric fields drop an ORA-1440 error
  301. *
  302. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  303. * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
  304. * @param string $skip_type_clause The type clause on alter columns, NULL by default.
  305. * @param string $skip_default_clause The default clause on alter columns, NULL by default.
  306. * @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
  307. * @return string The field altering SQL statement.
  308. */
  309. public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
  310. $skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
  311. $skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
  312. $skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
  313. $results = array(); // To store all the needed SQL commands
  314. // Get the quoted name of the table and field
  315. $tablename = $this->getTableName($xmldb_table);
  316. $fieldname = $xmldb_field->getName();
  317. // Take a look to field metadata
  318. $meta = $this->mdb->get_columns($xmldb_table->getName());
  319. $metac = $meta[$fieldname];
  320. $oldmetatype = $metac->meta_type;
  321. $oldlength = $metac->max_length;
  322. // To calculate the oldlength if the field is numeric, we need to perform one extra query
  323. // because ADOdb has one bug here. http://phplens.com/lens/lensforum/msgs.php?id=15883
  324. if ($oldmetatype == 'N') {
  325. $uppertablename = strtoupper($tablename);
  326. $upperfieldname = strtoupper($fieldname);
  327. if ($col = $this->mdb->get_record_sql("SELECT cname, precision
  328. FROM col
  329. WHERE tname = ? AND cname = ?",
  330. array($uppertablename, $upperfieldname))) {
  331. $oldlength = $col->precision;
  332. }
  333. }
  334. $olddecimals = empty($metac->scale) ? null : $metac->scale;
  335. $oldnotnull = empty($metac->not_null) ? false : $metac->not_null;
  336. $olddefault = empty($metac->default_value) || strtoupper($metac->default_value) == 'NULL' ? null : $metac->default_value;
  337. $typechanged = true; //By default, assume that the column type has changed
  338. $precisionchanged = true; //By default, assume that the column precision has changed
  339. $decimalchanged = true; //By default, assume that the column decimal has changed
  340. $defaultchanged = true; //By default, assume that the column default has changed
  341. $notnullchanged = true; //By default, assume that the column notnull has changed
  342. $from_temp_fields = false; //By default don't assume we are going to use temporal fields
  343. // Detect if we are changing the type of the column
  344. if (($xmldb_field->getType() == XMLDB_TYPE_INTEGER && $oldmetatype == 'I') ||
  345. ($xmldb_field->getType() == XMLDB_TYPE_NUMBER && $oldmetatype == 'N') ||
  346. ($xmldb_field->getType() == XMLDB_TYPE_FLOAT && $oldmetatype == 'F') ||
  347. ($xmldb_field->getType() == XMLDB_TYPE_CHAR && $oldmetatype == 'C') ||
  348. ($xmldb_field->getType() == XMLDB_TYPE_TEXT && $oldmetatype == 'X') ||
  349. ($xmldb_field->getType() == XMLDB_TYPE_BINARY && $oldmetatype == 'B')) {
  350. $typechanged = false;
  351. }
  352. // Detect if precision has changed
  353. if (($xmldb_field->getType() == XMLDB_TYPE_TEXT) ||
  354. ($xmldb_field->getType() == XMLDB_TYPE_BINARY) ||
  355. ($oldlength == -1) ||
  356. ($xmldb_field->getLength() == $oldlength)) {
  357. $precisionchanged = false;
  358. }
  359. // Detect if decimal has changed
  360. if (($xmldb_field->getType() == XMLDB_TYPE_INTEGER) ||
  361. ($xmldb_field->getType() == XMLDB_TYPE_CHAR) ||
  362. ($xmldb_field->getType() == XMLDB_TYPE_TEXT) ||
  363. ($xmldb_field->getType() == XMLDB_TYPE_BINARY) ||
  364. (!$xmldb_field->getDecimals()) ||
  365. (!$olddecimals) ||
  366. ($xmldb_field->getDecimals() == $olddecimals)) {
  367. $decimalchanged = false;
  368. }
  369. // Detect if we are changing the default
  370. if (($xmldb_field->getDefault() === null && $olddefault === null) ||
  371. ($xmldb_field->getDefault() === $olddefault) || //Check both equality and
  372. ("'" . $xmldb_field->getDefault() . "'" === $olddefault)) { //Equality with quotes because ADOdb returns the default with quotes
  373. $defaultchanged = false;
  374. }
  375. // Detect if we are changing the nullability
  376. if (($xmldb_field->getNotnull() === $oldnotnull)) {
  377. $notnullchanged = false;
  378. }
  379. // If type has changed or precision or decimal has changed and we are in one numeric field
  380. // - create one temp column with the new specs
  381. // - fill the new column with the values from the old one
  382. // - drop the old column
  383. // - rename the temp column to the original name
  384. if (($typechanged) || (($oldmetatype == 'N' || $oldmetatype == 'I') && ($precisionchanged || $decimalchanged))) {
  385. $tempcolname = $xmldb_field->getName() . '___tmp'; // Short tmp name, surely not conflicting ever
  386. if (strlen($tempcolname) > 30) { // Safeguard we don't excess the 30cc limit
  387. $tempcolname = 'ongoing_alter_column_tmp';
  388. }
  389. // Prevent temp field to have both NULL/NOT NULL and DEFAULT constraints
  390. $skip_notnull_clause = true;
  391. $skip_default_clause = true;
  392. $xmldb_field->setName($tempcolname);
  393. // Drop the temp column, in case it exists (due to one previous failure in conversion)
  394. // really ugly but we cannot enclose DDL into transaction :-(
  395. if (isset($meta[$tempcolname])) {
  396. $results = array_merge($results, $this->getDropFieldSQL($xmldb_table, $xmldb_field));
  397. }
  398. // Create the temporal column
  399. $results = array_merge($results, $this->getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause, $skip_type_clause, $skip_notnull_clause));
  400. // Copy contents from original col to the temporal one
  401. // From TEXT to integer/number we need explicit conversion
  402. if ($oldmetatype == 'X' && $xmldb_field->GetType() == XMLDB_TYPE_INTEGER) {
  403. $results[] = 'UPDATE ' . $tablename . ' SET ' . $tempcolname . ' = CAST(' . $this->mdb->sql_compare_text($fieldname) . ' AS INT)';
  404. } else if ($oldmetatype == 'X' && $xmldb_field->GetType() == XMLDB_TYPE_NUMBER) {
  405. $results[] = 'UPDATE ' . $tablename . ' SET ' . $tempcolname . ' = CAST(' . $this->mdb->sql_compare_text($fieldname) . ' AS NUMBER)';
  406. // Normal cases, implicit conversion
  407. } else {
  408. $results[] = 'UPDATE ' . $tablename . ' SET ' . $tempcolname . ' = ' . $fieldname;
  409. }
  410. // Drop the old column
  411. $xmldb_field->setName($fieldname); //Set back the original field name
  412. $results = array_merge($results, $this->getDropFieldSQL($xmldb_table, $xmldb_field));
  413. // Rename the temp column to the original one
  414. $results[] = 'ALTER TABLE ' . $tablename . ' RENAME COLUMN ' . $tempcolname . ' TO ' . $fieldname;
  415. // Mark we have performed one change based in temp fields
  416. $from_temp_fields = true;
  417. // Re-enable the notnull and default sections so the general AlterFieldSQL can use it
  418. $skip_notnull_clause = false;
  419. $skip_default_clause = false;
  420. // Disable the type section because we have done it with the temp field
  421. $skip_type_clause = true;
  422. // If new field is nullable, nullability hasn't changed
  423. if (!$xmldb_field->getNotnull()) {
  424. $notnullchanged = false;
  425. }
  426. // If new field hasn't default, default hasn't changed
  427. if ($xmldb_field->getDefault() === null) {
  428. $defaultchanged = false;
  429. }
  430. }
  431. // If type and precision and decimals hasn't changed, prevent the type clause
  432. if (!$typechanged && !$precisionchanged && !$decimalchanged) {
  433. $skip_type_clause = true;
  434. }
  435. // If NULL/NOT NULL hasn't changed
  436. // prevent null clause to be specified
  437. if (!$notnullchanged) {
  438. $skip_notnull_clause = true; // Initially, prevent the notnull clause
  439. // But, if we have used the temp field and the new field is not null, then enforce the not null clause
  440. if ($from_temp_fields && $xmldb_field->getNotnull()) {
  441. $skip_notnull_clause = false;
  442. }
  443. }
  444. // If default hasn't changed
  445. // prevent default clause to be specified
  446. if (!$defaultchanged) {
  447. $skip_default_clause = true; // Initially, prevent the default clause
  448. // But, if we have used the temp field and the new field has default clause, then enforce the default clause
  449. if ($from_temp_fields) {
  450. $default_clause = $this->getDefaultClause($xmldb_field);
  451. if ($default_clause) {
  452. $skip_notnull_clause = false;
  453. }
  454. }
  455. }
  456. // If arriving here, something is not being skipped (type, notnull, default), calculate the standard AlterFieldSQL
  457. if (!$skip_type_clause || !$skip_notnull_clause || !$skip_default_clause) {
  458. $results = array_merge($results, parent::getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause, $skip_default_clause, $skip_notnull_clause));
  459. return $results;
  460. }
  461. // Finally return results
  462. return $results;
  463. }
  464. /**
  465. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to add its default
  466. * (usually invoked from getModifyDefaultSQL()
  467. *
  468. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  469. * @param xmldb_field $xmldb_field The xmldb_field object instance.
  470. * @return array Array of SQL statements to create a field's default.
  471. */
  472. public function getCreateDefaultSQL($xmldb_table, $xmldb_field) {
  473. // Just a wrapper over the getAlterFieldSQL() function for Oracle that
  474. // is capable of handling defaults
  475. return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
  476. }
  477. /**
  478. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop its default
  479. * (usually invoked from getModifyDefaultSQL()
  480. *
  481. * Note that this method may be dropped in future.
  482. *
  483. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  484. * @param xmldb_field $xmldb_field The xmldb_field object instance.
  485. * @return array Array of SQL statements to create a field's default.
  486. *
  487. * @todo MDL-31147 Moodle 2.1 - Drop getDropDefaultSQL()
  488. */
  489. public function getDropDefaultSQL($xmldb_table, $xmldb_field) {
  490. // Just a wrapper over the getAlterFieldSQL() function for Oracle that
  491. // is capable of handling defaults
  492. return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
  493. }
  494. /**
  495. * Given one xmldb_table returns one string with the sequence of the table
  496. * in the table (fetched from DB)
  497. * The sequence name for oracle is calculated by looking the corresponding
  498. * trigger and retrieving the sequence name from it (because sequences are
  499. * independent elements)
  500. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  501. * @return string|bool If no sequence is found, returns false
  502. */
  503. public function getSequenceFromDB($xmldb_table) {
  504. $tablename = strtoupper($this->getTableName($xmldb_table));
  505. $prefixupper = strtoupper($this->prefix);
  506. $sequencename = false;
  507. if ($trigger = $this->mdb->get_record_sql("SELECT trigger_name, trigger_body
  508. FROM user_triggers
  509. WHERE table_name = ? AND trigger_name LIKE ?",
  510. array($tablename, "{$prefixupper}%_ID%_TRG"))) {
  511. // If trigger found, regexp it looking for the sequence name
  512. preg_match('/.*SELECT (.*)\.nextval/i', $trigger->trigger_body, $matches);
  513. if (isset($matches[1])) {
  514. $sequencename = $matches[1];
  515. }
  516. }
  517. return $sequencename;
  518. }
  519. /**
  520. * Given one xmldb_table returns one string with the trigger
  521. * in the table (fetched from DB)
  522. *
  523. * @param xmldb_table $xmldb_table The xmldb_table object instance.
  524. * @return string|bool If no trigger is found, returns false
  525. */
  526. public function getTriggerFromDB($xmldb_table) {
  527. $tablename = strtoupper($this->getTableName($xmldb_table));
  528. $prefixupper = strtoupper($this->prefix);
  529. $triggername = false;
  530. if ($trigger = $this->mdb->get_record_sql("SELECT trigger_name, trigger_body
  531. FROM user_triggers
  532. WHERE table_name = ? AND trigger_name LIKE ?",
  533. array($tablename, "{$prefixupper}%_ID%_TRG"))) {
  534. $triggername = $trigger->trigger_name;
  535. }
  536. return $triggername;
  537. }
  538. /**
  539. * Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg).
  540. *
  541. * (MySQL requires the whole xmldb_table object to be specified, so we add it always)
  542. *
  543. * This is invoked from getNameForObject().
  544. * Only some DB have this implemented.
  545. *
  546. * @param string $object_name The object's name to check for.
  547. * @param string $type The object's type (pk, uk, fk, ck, ix, uix, seq, trg).
  548. * @param string $table_name The table's name to check in
  549. * @return bool If such name is currently in use (true) or no (false)
  550. */
  551. public function isNameInUse($object_name, $type, $table_name) {
  552. switch($type) {
  553. case 'ix':
  554. case 'uix':
  555. case 'seq':
  556. case 'trg':
  557. if ($check = $this->mdb->get_records_sql("SELECT object_name
  558. FROM user_objects
  559. WHERE lower(object_name) = ?", array(strtolower($object_name)))) {
  560. return true;
  561. }
  562. break;
  563. case 'pk':
  564. case 'uk':
  565. case 'fk':
  566. case 'ck':
  567. if ($check = $this->mdb->get_records_sql("SELECT constraint_name
  568. FROM user_constraints
  569. WHERE lower(constraint_name) = ?", array(strtolower($object_name)))) {
  570. return true;
  571. }
  572. break;
  573. }
  574. return false; //No name in use found
  575. }
  576. /**
  577. * Adds slashes to string.
  578. * @param string $s
  579. * @return string The escaped string.
  580. */
  581. public function addslashes($s) {
  582. // do not use php addslashes() because it depends on PHP quote settings!
  583. $s = str_replace("'", "''", $s);
  584. return $s;
  585. }
  586. /**
  587. * Returns an array of reserved words (lowercase) for this DB
  588. * @return array An array of database specific reserved words
  589. */
  590. public static function getReservedWords() {
  591. // This file contains the reserved words for Oracle databases
  592. // from http://download-uk.oracle.com/docs/cd/B10501_01/server.920/a96540/ap_keywd.htm
  593. $reserved_words = array (
  594. 'access', 'add', 'all', 'alter', 'and', 'any',
  595. 'as', 'asc', 'audit', 'between', 'by', 'char',
  596. 'check', 'cluster', 'column', 'comment',
  597. 'compress', 'connect', 'create', 'current',
  598. 'date', 'decimal', 'default', 'delete', 'desc',
  599. 'distinct', 'drop', 'else', 'exclusive', 'exists',
  600. 'file', 'float', 'for', 'from', 'grant', 'group',
  601. 'having', 'identified', 'immediate', 'in',
  602. 'increment', 'index', 'initial', 'insert',
  603. 'integer', 'intersect', 'into', 'is', 'level',
  604. 'like', 'lock', 'long', 'maxextents', 'minus',
  605. 'mlslabel', 'mode', 'modify', 'nchar', 'nclob', 'noaudit',
  606. 'nocompress', 'not', 'nowait', 'null', 'number', 'nvarchar2',
  607. 'of', 'offline', 'on', 'online', 'option', 'or',
  608. 'order', 'pctfree', 'prior', 'privileges',
  609. 'public', 'raw', 'rename', 'resource', 'revoke',
  610. 'row', 'rowid', 'rownum', 'rows', 'select',
  611. 'session', 'set', 'share', 'size', 'smallint',
  612. 'start', 'successful', 'synonym', 'sysdate',
  613. 'table', 'then', 'to', 'trigger', 'uid', 'union',
  614. 'unique', 'update', 'user', 'validate', 'values',
  615. 'varchar', 'varchar2', 'view', 'whenever',
  616. 'where', 'with'
  617. );
  618. return $reserved_words;
  619. }
  620. }