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