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