1 |
efrain |
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 |
/**
|
|
|
18 |
* This class represent the base generator class where all the needed functions to generate proper SQL are defined.
|
|
|
19 |
*
|
|
|
20 |
* The rest of classes will inherit, by default, the same logic.
|
|
|
21 |
* Functions will be overridden as needed to generate correct SQL.
|
|
|
22 |
*
|
|
|
23 |
* @package core_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 |
*/
|
|
|
28 |
|
|
|
29 |
defined('MOODLE_INTERNAL') || die();
|
|
|
30 |
|
|
|
31 |
/**
|
|
|
32 |
* Abstract sql generator class, base for all db specific implementations.
|
|
|
33 |
*
|
|
|
34 |
* @package core_ddl
|
|
|
35 |
* @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
|
|
|
36 |
* 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
|
|
|
37 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
38 |
*/
|
|
|
39 |
abstract class sql_generator {
|
|
|
40 |
|
|
|
41 |
// Please, avoid editing this defaults in this base class!
|
|
|
42 |
// It could change the behaviour of the rest of generators
|
|
|
43 |
// that, by default, inherit this configuration.
|
|
|
44 |
// To change any of them, do it in extended classes instead.
|
|
|
45 |
|
|
|
46 |
/** @var string Used to quote names. */
|
|
|
47 |
public $quote_string = '"';
|
|
|
48 |
|
|
|
49 |
/** @var string To be automatically added at the end of each statement. */
|
|
|
50 |
public $statement_end = ';';
|
|
|
51 |
|
|
|
52 |
/** @var bool To decide if we want to quote all the names or only the reserved ones. */
|
|
|
53 |
public $quote_all = false;
|
|
|
54 |
|
|
|
55 |
/** @var bool To create all the integers as NUMBER(x) (also called DECIMAL, NUMERIC...). */
|
|
|
56 |
public $integer_to_number = false;
|
|
|
57 |
|
|
|
58 |
/** @var bool To create all the floats as NUMBER(x) (also called DECIMAL, NUMERIC...). */
|
|
|
59 |
public $float_to_number = false;
|
|
|
60 |
|
|
|
61 |
/** @var string Proper type for NUMBER(x) in this DB. */
|
|
|
62 |
public $number_type = 'NUMERIC';
|
|
|
63 |
|
|
|
64 |
/** @var string To define the default to set for NOT NULLs CHARs without default (null=do nothing).*/
|
|
|
65 |
public $default_for_char = null;
|
|
|
66 |
|
|
|
67 |
/** @var bool To specify if the generator must use some DEFAULT clause to drop defaults.*/
|
|
|
68 |
public $drop_default_value_required = false;
|
|
|
69 |
|
|
|
70 |
/** @var string The DEFAULT clause required to drop defaults.*/
|
|
|
71 |
public $drop_default_value = '';
|
|
|
72 |
|
|
|
73 |
/** @var bool To decide if the default clause of each field must go after the null clause.*/
|
|
|
74 |
public $default_after_null = true;
|
|
|
75 |
|
|
|
76 |
/** @var bool To force the generator if NULL clauses must be specified. It shouldn't be necessary.*/
|
|
|
77 |
public $specify_nulls = false;
|
|
|
78 |
|
|
|
79 |
/** @var string To force primary key names to one string (null=no force).*/
|
|
|
80 |
public $primary_key_name = null;
|
|
|
81 |
|
|
|
82 |
/** @var bool True if the generator builds primary keys.*/
|
|
|
83 |
public $primary_keys = true;
|
|
|
84 |
|
|
|
85 |
/** @var bool True if the generator builds unique keys.*/
|
|
|
86 |
public $unique_keys = false;
|
|
|
87 |
|
|
|
88 |
/** @var bool True if the generator builds foreign keys.*/
|
|
|
89 |
public $foreign_keys = false;
|
|
|
90 |
|
|
|
91 |
/** @var string Template to drop PKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
|
92 |
public $drop_primary_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
93 |
|
|
|
94 |
/** @var string Template to drop UKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
|
95 |
public $drop_unique_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
96 |
|
|
|
97 |
/** @var string Template to drop FKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
|
98 |
public $drop_foreign_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
99 |
|
|
|
100 |
/** @var bool True if the generator needs to add extra code to generate the sequence fields.*/
|
|
|
101 |
public $sequence_extra_code = true;
|
|
|
102 |
|
|
|
103 |
/** @var string The particular name for inline sequences in this generator.*/
|
|
|
104 |
public $sequence_name = 'auto_increment';
|
|
|
105 |
|
|
|
106 |
/** @var string|bool Different name for small (4byte) sequences or false if same.*/
|
|
|
107 |
public $sequence_name_small = false;
|
|
|
108 |
|
|
|
109 |
/**
|
|
|
110 |
* @var bool To avoid outputting the rest of the field specs, leaving only the name and the sequence_name returned.
|
|
|
111 |
* @see getFieldSQL()
|
|
|
112 |
*/
|
|
|
113 |
public $sequence_only = false;
|
|
|
114 |
|
|
|
115 |
/** @var bool True if the generator needs to add code for table comments.*/
|
|
|
116 |
public $add_table_comments = true;
|
|
|
117 |
|
|
|
118 |
/** @var bool True if the generator needs to add the after clause for fields.*/
|
|
|
119 |
public $add_after_clause = false;
|
|
|
120 |
|
|
|
121 |
/**
|
|
|
122 |
* @var bool True if the generator needs to prepend the prefix to all the key/index/sequence/trigger/check names.
|
|
|
123 |
* @see $prefix
|
|
|
124 |
*/
|
|
|
125 |
public $prefix_on_names = true;
|
|
|
126 |
|
|
|
127 |
/** @var int Maximum length for key/index/sequence/trigger/check names (keep 30 for all!).*/
|
|
|
128 |
public $names_max_length = 30;
|
|
|
129 |
|
|
|
130 |
/** @var string Characters to be used as concatenation operator. If not defined, MySQL CONCAT function will be used.*/
|
|
|
131 |
public $concat_character = '||';
|
|
|
132 |
|
|
|
133 |
/** @var string SQL sentence to rename one table, both 'OLDNAME' and 'NEWNAME' keywords are dynamically replaced.*/
|
|
|
134 |
public $rename_table_sql = 'ALTER TABLE OLDNAME RENAME TO NEWNAME';
|
|
|
135 |
|
|
|
136 |
/** @var string SQL sentence to drop one table where the 'TABLENAME' keyword is dynamically replaced.*/
|
|
|
137 |
public $drop_table_sql = 'DROP TABLE TABLENAME';
|
|
|
138 |
|
|
|
139 |
/** @var string The SQL template to alter columns where the 'TABLENAME' and 'COLUMNSPECS' keywords are dynamically replaced.*/
|
|
|
140 |
public $alter_column_sql = 'ALTER TABLE TABLENAME ALTER COLUMN COLUMNSPECS';
|
|
|
141 |
|
|
|
142 |
/** @var bool The generator will skip the default clause on alter columns.*/
|
|
|
143 |
public $alter_column_skip_default = false;
|
|
|
144 |
|
|
|
145 |
/** @var bool The generator will skip the type clause on alter columns.*/
|
|
|
146 |
public $alter_column_skip_type = false;
|
|
|
147 |
|
|
|
148 |
/** @var bool The generator will skip the null/notnull clause on alter columns.*/
|
|
|
149 |
public $alter_column_skip_notnull = false;
|
|
|
150 |
|
|
|
151 |
/** @var string SQL sentence to rename one column where 'TABLENAME', 'OLDFIELDNAME' and 'NEWFIELDNAME' keywords are dynamically replaced.*/
|
|
|
152 |
public $rename_column_sql = 'ALTER TABLE TABLENAME RENAME COLUMN OLDFIELDNAME TO NEWFIELDNAME';
|
|
|
153 |
|
|
|
154 |
/** @var string SQL sentence to drop one index where 'TABLENAME', 'INDEXNAME' keywords are dynamically replaced.*/
|
|
|
155 |
public $drop_index_sql = 'DROP INDEX INDEXNAME';
|
|
|
156 |
|
|
|
157 |
/** @var string SQL sentence to rename one index where 'TABLENAME', 'OLDINDEXNAME' and 'NEWINDEXNAME' are dynamically replaced.*/
|
|
|
158 |
public $rename_index_sql = 'ALTER INDEX OLDINDEXNAME RENAME TO NEWINDEXNAME';
|
|
|
159 |
|
|
|
160 |
/** @var string SQL sentence to rename one key 'TABLENAME', 'OLDKEYNAME' and 'NEWKEYNAME' are dynamically replaced.*/
|
|
|
161 |
public $rename_key_sql = 'ALTER TABLE TABLENAME CONSTRAINT OLDKEYNAME RENAME TO NEWKEYNAME';
|
|
|
162 |
|
|
|
163 |
/** @var string The prefix to be used for all the DB objects.*/
|
|
|
164 |
public $prefix;
|
|
|
165 |
|
|
|
166 |
/** @var array List of reserved words (in order to quote them properly).*/
|
|
|
167 |
public $reserved_words;
|
|
|
168 |
|
|
|
169 |
/** @var moodle_database The moodle_database instance.*/
|
|
|
170 |
public $mdb;
|
|
|
171 |
|
|
|
172 |
/** @var Control existing temptables.*/
|
|
|
173 |
protected $temptables;
|
|
|
174 |
|
|
|
175 |
/**
|
|
|
176 |
* Creates a new sql_generator.
|
|
|
177 |
* @param moodle_database $mdb The moodle_database object instance.
|
|
|
178 |
* @param moodle_temptables $temptables The optional moodle_temptables instance, null by default.
|
|
|
179 |
*/
|
|
|
180 |
public function __construct($mdb, $temptables = null) {
|
|
|
181 |
$this->prefix = $mdb->get_prefix();
|
|
|
182 |
$this->reserved_words = $this->getReservedWords();
|
|
|
183 |
$this->mdb = $mdb; // this creates circular reference - the other link must be unset when closing db
|
|
|
184 |
$this->temptables = $temptables;
|
|
|
185 |
}
|
|
|
186 |
|
|
|
187 |
/**
|
|
|
188 |
* Releases all resources.
|
|
|
189 |
*/
|
|
|
190 |
public function dispose() {
|
|
|
191 |
$this->mdb = null;
|
|
|
192 |
}
|
|
|
193 |
|
|
|
194 |
/**
|
|
|
195 |
* Given one string (or one array), ends it with $statement_end .
|
|
|
196 |
*
|
|
|
197 |
* @see $statement_end
|
|
|
198 |
*
|
|
|
199 |
* @param array|string $input SQL statement(s).
|
|
|
200 |
* @return array|string
|
|
|
201 |
*/
|
|
|
202 |
public function getEndedStatements($input) {
|
|
|
203 |
|
|
|
204 |
if (is_array($input)) {
|
|
|
205 |
foreach ($input as $key=>$content) {
|
|
|
206 |
$input[$key] = $this->getEndedStatements($content);
|
|
|
207 |
}
|
|
|
208 |
return $input;
|
|
|
209 |
} else {
|
|
|
210 |
$input = trim($input).$this->statement_end;
|
|
|
211 |
return $input;
|
|
|
212 |
}
|
|
|
213 |
}
|
|
|
214 |
|
|
|
215 |
/**
|
|
|
216 |
* Given one xmldb_table, checks if it exists in DB (true/false).
|
|
|
217 |
*
|
|
|
218 |
* @param mixed $table The table to be searched (string name or xmldb_table instance).
|
|
|
219 |
* @return boolean true/false
|
|
|
220 |
*/
|
|
|
221 |
public function table_exists($table) {
|
|
|
222 |
if (is_string($table)) {
|
|
|
223 |
$tablename = $table;
|
|
|
224 |
} else {
|
|
|
225 |
// Calculate the name of the table
|
|
|
226 |
$tablename = $table->getName();
|
|
|
227 |
}
|
|
|
228 |
|
|
|
229 |
if ($this->temptables->is_temptable($tablename)) {
|
|
|
230 |
return true;
|
|
|
231 |
}
|
|
|
232 |
|
|
|
233 |
// Get all tables in moodle database.
|
|
|
234 |
$tables = $this->mdb->get_tables();
|
|
|
235 |
return isset($tables[$tablename]);
|
|
|
236 |
}
|
|
|
237 |
|
|
|
238 |
/**
|
|
|
239 |
* This function will return the SQL code needed to create db tables and statements.
|
|
|
240 |
* @see xmldb_structure
|
|
|
241 |
*
|
|
|
242 |
* @param xmldb_structure $xmldb_structure An xmldb_structure instance.
|
|
|
243 |
* @return array
|
|
|
244 |
*/
|
|
|
245 |
public function getCreateStructureSQL($xmldb_structure) {
|
|
|
246 |
$results = array();
|
|
|
247 |
|
|
|
248 |
if ($tables = $xmldb_structure->getTables()) {
|
|
|
249 |
foreach ($tables as $table) {
|
|
|
250 |
$results = array_merge($results, $this->getCreateTableSQL($table));
|
|
|
251 |
}
|
|
|
252 |
}
|
|
|
253 |
|
|
|
254 |
return $results;
|
|
|
255 |
}
|
|
|
256 |
|
|
|
257 |
/**
|
|
|
258 |
* Given one xmldb_table, this returns it's correct name, depending of all the parameterization.
|
|
|
259 |
* eg: This appends $prefix to the table name.
|
|
|
260 |
*
|
|
|
261 |
* @see $prefix
|
|
|
262 |
*
|
|
|
263 |
* @param xmldb_table $xmldb_table The table whose name we want.
|
|
|
264 |
* @param boolean $quoted To specify if the name must be quoted (if reserved word, only!).
|
|
|
265 |
* @return string The correct name of the table.
|
|
|
266 |
*/
|
|
|
267 |
public function getTableName(xmldb_table $xmldb_table, $quoted=true) {
|
|
|
268 |
// Get the name
|
|
|
269 |
$tablename = $this->prefix.$xmldb_table->getName();
|
|
|
270 |
|
|
|
271 |
// Apply quotes optionally
|
|
|
272 |
if ($quoted) {
|
|
|
273 |
$tablename = $this->getEncQuoted($tablename);
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
return $tablename;
|
|
|
277 |
}
|
|
|
278 |
|
|
|
279 |
/**
|
|
|
280 |
* Given one correct xmldb_table, returns the SQL statements
|
|
|
281 |
* to create it (inside one array).
|
|
|
282 |
*
|
|
|
283 |
* @param xmldb_table $xmldb_table An xmldb_table instance.
|
|
|
284 |
* @return array An array of SQL statements, starting with the table creation SQL followed
|
|
|
285 |
* by any of its comments, indexes and sequence creation SQL statements.
|
|
|
286 |
*/
|
|
|
287 |
public function getCreateTableSQL($xmldb_table) {
|
|
|
288 |
if ($error = $xmldb_table->validateDefinition()) {
|
|
|
289 |
throw new coding_exception($error);
|
|
|
290 |
}
|
|
|
291 |
|
|
|
292 |
$results = array(); //Array where all the sentences will be stored
|
|
|
293 |
|
|
|
294 |
// Table header
|
|
|
295 |
$table = 'CREATE TABLE ' . $this->getTableName($xmldb_table) . ' (';
|
|
|
296 |
|
|
|
297 |
if (!$xmldb_fields = $xmldb_table->getFields()) {
|
|
|
298 |
return $results;
|
|
|
299 |
}
|
|
|
300 |
|
|
|
301 |
$sequencefield = null;
|
|
|
302 |
|
|
|
303 |
// Add the fields, separated by commas
|
|
|
304 |
foreach ($xmldb_fields as $xmldb_field) {
|
|
|
305 |
if ($xmldb_field->getSequence()) {
|
|
|
306 |
$sequencefield = $xmldb_field->getName();
|
|
|
307 |
}
|
|
|
308 |
$table .= "\n " . $this->getFieldSQL($xmldb_table, $xmldb_field);
|
|
|
309 |
$table .= ',';
|
|
|
310 |
}
|
|
|
311 |
// Add the keys, separated by commas
|
|
|
312 |
if ($xmldb_keys = $xmldb_table->getKeys()) {
|
|
|
313 |
foreach ($xmldb_keys as $xmldb_key) {
|
|
|
314 |
if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
|
315 |
$table .= "\nCONSTRAINT " . $keytext . ',';
|
|
|
316 |
}
|
|
|
317 |
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
|
|
|
318 |
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
|
|
|
319 |
//Duplicate the key
|
|
|
320 |
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
|
321 |
if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
|
322 |
$table .= "\nCONSTRAINT " . $keytext . ',';
|
|
|
323 |
}
|
|
|
324 |
}
|
|
|
325 |
// make sure sequence field is unique
|
|
|
326 |
if ($sequencefield and $xmldb_key->getType() == XMLDB_KEY_PRIMARY) {
|
|
|
327 |
$fields = $xmldb_key->getFields();
|
|
|
328 |
$field = reset($fields);
|
|
|
329 |
if ($sequencefield === $field) {
|
|
|
330 |
$sequencefield = null;
|
|
|
331 |
}
|
|
|
332 |
}
|
|
|
333 |
}
|
|
|
334 |
}
|
|
|
335 |
// throw error if sequence field does not have unique key defined
|
|
|
336 |
if ($sequencefield) {
|
|
|
337 |
throw new ddl_exception('ddsequenceerror', $xmldb_table->getName());
|
|
|
338 |
}
|
|
|
339 |
|
|
|
340 |
// Table footer, trim the latest comma
|
|
|
341 |
$table = trim($table,',');
|
|
|
342 |
$table .= "\n)";
|
|
|
343 |
|
|
|
344 |
// Add the CREATE TABLE to results
|
|
|
345 |
$results[] = $table;
|
|
|
346 |
|
|
|
347 |
// Add comments if specified and it exists
|
|
|
348 |
if ($this->add_table_comments && $xmldb_table->getComment()) {
|
|
|
349 |
$comment = $this->getCommentSQL($xmldb_table);
|
|
|
350 |
// Add the COMMENT to results
|
|
|
351 |
$results = array_merge($results, $comment);
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
// Add the indexes (each one, one statement)
|
|
|
355 |
if ($xmldb_indexes = $xmldb_table->getIndexes()) {
|
|
|
356 |
foreach ($xmldb_indexes as $xmldb_index) {
|
|
|
357 |
//tables do not exist yet, which means indexed can not exist yet
|
|
|
358 |
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $xmldb_index)) {
|
|
|
359 |
$results = array_merge($results, $indextext);
|
|
|
360 |
}
|
|
|
361 |
}
|
|
|
362 |
}
|
|
|
363 |
|
|
|
364 |
// Also, add the indexes needed from keys, based on configuration (each one, one statement)
|
|
|
365 |
if ($xmldb_keys = $xmldb_table->getKeys()) {
|
|
|
366 |
foreach ($xmldb_keys as $xmldb_key) {
|
|
|
367 |
// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
|
368 |
// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
|
|
|
369 |
if (!$this->getKeySQL($xmldb_table, $xmldb_key) || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
|
370 |
// Create the interim index
|
|
|
371 |
$index = new xmldb_index('anyname');
|
|
|
372 |
$index->setFields($xmldb_key->getFields());
|
|
|
373 |
//tables do not exist yet, which means indexed can not exist yet
|
|
|
374 |
$createindex = false; //By default
|
|
|
375 |
switch ($xmldb_key->getType()) {
|
|
|
376 |
case XMLDB_KEY_UNIQUE:
|
|
|
377 |
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
|
378 |
$index->setUnique(true);
|
|
|
379 |
$createindex = true;
|
|
|
380 |
break;
|
|
|
381 |
case XMLDB_KEY_FOREIGN:
|
|
|
382 |
$index->setUnique(false);
|
|
|
383 |
$createindex = true;
|
|
|
384 |
break;
|
|
|
385 |
}
|
|
|
386 |
if ($createindex) {
|
|
|
387 |
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $index)) {
|
|
|
388 |
// Add the INDEX to the array
|
|
|
389 |
$results = array_merge($results, $indextext);
|
|
|
390 |
}
|
|
|
391 |
}
|
|
|
392 |
}
|
|
|
393 |
}
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
// Add sequence extra code if needed
|
|
|
397 |
if ($this->sequence_extra_code) {
|
|
|
398 |
// Iterate over fields looking for sequences
|
|
|
399 |
foreach ($xmldb_fields as $xmldb_field) {
|
|
|
400 |
if ($xmldb_field->getSequence()) {
|
|
|
401 |
// returns an array of statements needed to create one sequence
|
|
|
402 |
$sequence_sentences = $this->getCreateSequenceSQL($xmldb_table, $xmldb_field);
|
|
|
403 |
// Add the SEQUENCE to the array
|
|
|
404 |
$results = array_merge($results, $sequence_sentences);
|
|
|
405 |
}
|
|
|
406 |
}
|
|
|
407 |
}
|
|
|
408 |
|
|
|
409 |
return $results;
|
|
|
410 |
}
|
|
|
411 |
|
|
|
412 |
/**
|
|
|
413 |
* Given one correct xmldb_index, returns the SQL statements
|
|
|
414 |
* needed to create it (in array).
|
|
|
415 |
*
|
|
|
416 |
* @param xmldb_table $xmldb_table The xmldb_table instance to create the index on.
|
|
|
417 |
* @param xmldb_index $xmldb_index The xmldb_index to create.
|
|
|
418 |
* @return array An array of SQL statements to create the index.
|
|
|
419 |
* @throws coding_exception Thrown if the xmldb_index does not validate with the xmldb_table.
|
|
|
420 |
*/
|
|
|
421 |
public function getCreateIndexSQL($xmldb_table, $xmldb_index) {
|
|
|
422 |
if ($error = $xmldb_index->validateDefinition($xmldb_table)) {
|
|
|
423 |
throw new coding_exception($error);
|
|
|
424 |
}
|
|
|
425 |
|
|
|
426 |
$unique = '';
|
|
|
427 |
$suffix = 'ix';
|
|
|
428 |
if ($xmldb_index->getUnique()) {
|
|
|
429 |
$unique = ' UNIQUE';
|
|
|
430 |
$suffix = 'uix';
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
$index = 'CREATE' . $unique . ' INDEX ';
|
|
|
434 |
$index .= $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_index->getFields()), $suffix);
|
|
|
435 |
$index .= ' ON ' . $this->getTableName($xmldb_table);
|
|
|
436 |
$index .= ' (' . implode(', ', $this->getEncQuoted($xmldb_index->getFields())) . ')';
|
|
|
437 |
|
|
|
438 |
return array($index);
|
|
|
439 |
}
|
|
|
440 |
|
|
|
441 |
/**
|
|
|
442 |
* Given one correct xmldb_field, returns the complete SQL line to create it.
|
|
|
443 |
*
|
|
|
444 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
445 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
|
446 |
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
|
447 |
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
|
448 |
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
|
449 |
* @param string $specify_nulls_clause To force a specific null clause, NULL by default.
|
|
|
450 |
* @param bool $specify_field_name Flag to specify fieldname in return.
|
|
|
451 |
* @return string The field generating SQL statement.
|
|
|
452 |
* @throws coding_exception Thrown when xmldb_field doesn't validate with the xmldb_table.
|
|
|
453 |
*/
|
|
|
454 |
public function getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL, $specify_nulls_clause = NULL, $specify_field_name = true) {
|
|
|
455 |
if ($error = $xmldb_field->validateDefinition($xmldb_table)) {
|
|
|
456 |
throw new coding_exception($error);
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
|
460 |
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
|
461 |
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
|
462 |
$specify_nulls_clause = is_null($specify_nulls_clause) ? $this->specify_nulls : $specify_nulls_clause;
|
|
|
463 |
|
|
|
464 |
// First of all, convert integers to numbers if defined
|
|
|
465 |
if ($this->integer_to_number) {
|
|
|
466 |
if ($xmldb_field->getType() == XMLDB_TYPE_INTEGER) {
|
|
|
467 |
$xmldb_field->setType(XMLDB_TYPE_NUMBER);
|
|
|
468 |
}
|
|
|
469 |
}
|
|
|
470 |
// Same for floats
|
|
|
471 |
if ($this->float_to_number) {
|
|
|
472 |
if ($xmldb_field->getType() == XMLDB_TYPE_FLOAT) {
|
|
|
473 |
$xmldb_field->setType(XMLDB_TYPE_NUMBER);
|
|
|
474 |
}
|
|
|
475 |
}
|
|
|
476 |
|
|
|
477 |
$field = ''; // Let's accumulate the whole expression based on params and settings
|
|
|
478 |
// The name
|
|
|
479 |
if ($specify_field_name) {
|
|
|
480 |
$field .= $this->getEncQuoted($xmldb_field->getName());
|
|
|
481 |
}
|
|
|
482 |
// The type and length only if we don't want to skip it
|
|
|
483 |
if (!$skip_type_clause) {
|
|
|
484 |
// The type and length
|
|
|
485 |
$field .= ' ' . $this->getTypeSQL($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
|
|
|
486 |
}
|
|
|
487 |
// note: unsigned is not supported any more since moodle 2.3, all numbers are signed
|
|
|
488 |
// Calculate the not null clause
|
|
|
489 |
$notnull = '';
|
|
|
490 |
// Only if we don't want to skip it
|
|
|
491 |
if (!$skip_notnull_clause) {
|
|
|
492 |
if ($xmldb_field->getNotNull()) {
|
|
|
493 |
$notnull = ' NOT NULL';
|
|
|
494 |
} else {
|
|
|
495 |
if ($specify_nulls_clause) {
|
|
|
496 |
$notnull = ' NULL';
|
|
|
497 |
}
|
|
|
498 |
}
|
|
|
499 |
}
|
|
|
500 |
// Calculate the default clause
|
|
|
501 |
$default_clause = '';
|
|
|
502 |
if (!$skip_default_clause) { //Only if we don't want to skip it
|
|
|
503 |
$default_clause = $this->getDefaultClause($xmldb_field);
|
|
|
504 |
}
|
|
|
505 |
// Based on default_after_null, set both clauses properly
|
|
|
506 |
if ($this->default_after_null) {
|
|
|
507 |
$field .= $notnull . $default_clause;
|
|
|
508 |
} else {
|
|
|
509 |
$field .= $default_clause . $notnull;
|
|
|
510 |
}
|
|
|
511 |
// The sequence
|
|
|
512 |
if ($xmldb_field->getSequence()) {
|
|
|
513 |
if($xmldb_field->getLength()<=9 && $this->sequence_name_small) {
|
|
|
514 |
$sequencename=$this->sequence_name_small;
|
|
|
515 |
} else {
|
|
|
516 |
$sequencename=$this->sequence_name;
|
|
|
517 |
}
|
|
|
518 |
$field .= ' ' . $sequencename;
|
|
|
519 |
if ($this->sequence_only) {
|
|
|
520 |
// We only want the field name and sequence name to be printed
|
|
|
521 |
// so, calculate it and return
|
|
|
522 |
$sql = $this->getEncQuoted($xmldb_field->getName()) . ' ' . $sequencename;
|
|
|
523 |
return $sql;
|
|
|
524 |
}
|
|
|
525 |
}
|
|
|
526 |
return $field;
|
|
|
527 |
}
|
|
|
528 |
|
|
|
529 |
/**
|
|
|
530 |
* Given one correct xmldb_key, returns its specs.
|
|
|
531 |
*
|
|
|
532 |
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
|
533 |
* @param xmldb_key $xmldb_key The xmldb_key's specifications requested.
|
|
|
534 |
* @return string SQL statement about the xmldb_key.
|
|
|
535 |
*/
|
|
|
536 |
public function getKeySQL($xmldb_table, $xmldb_key) {
|
|
|
537 |
|
|
|
538 |
$key = '';
|
|
|
539 |
|
|
|
540 |
switch ($xmldb_key->getType()) {
|
|
|
541 |
case XMLDB_KEY_PRIMARY:
|
|
|
542 |
if ($this->primary_keys) {
|
|
|
543 |
if ($this->primary_key_name !== null) {
|
|
|
544 |
$key = $this->getEncQuoted($this->primary_key_name);
|
|
|
545 |
} else {
|
|
|
546 |
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'pk');
|
|
|
547 |
}
|
|
|
548 |
$key .= ' PRIMARY KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
|
549 |
}
|
|
|
550 |
break;
|
|
|
551 |
case XMLDB_KEY_UNIQUE:
|
|
|
552 |
if ($this->unique_keys) {
|
|
|
553 |
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'uk');
|
|
|
554 |
$key .= ' UNIQUE (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
|
555 |
}
|
|
|
556 |
break;
|
|
|
557 |
case XMLDB_KEY_FOREIGN:
|
|
|
558 |
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
|
559 |
if ($this->foreign_keys) {
|
|
|
560 |
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'fk');
|
|
|
561 |
$key .= ' FOREIGN KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
|
562 |
$key .= ' REFERENCES ' . $this->getEncQuoted($this->prefix . $xmldb_key->getRefTable());
|
|
|
563 |
$key .= ' (' . implode(', ', $this->getEncQuoted($xmldb_key->getRefFields())) . ')';
|
|
|
564 |
}
|
|
|
565 |
break;
|
|
|
566 |
}
|
|
|
567 |
|
|
|
568 |
return $key;
|
|
|
569 |
}
|
|
|
570 |
|
|
|
571 |
/**
|
|
|
572 |
* Give one xmldb_field, returns the correct "default value" for the current configuration
|
|
|
573 |
*
|
|
|
574 |
* @param xmldb_field $xmldb_field The field.
|
|
|
575 |
* @return The default value of the field.
|
|
|
576 |
*/
|
|
|
577 |
public function getDefaultValue($xmldb_field) {
|
|
|
578 |
|
|
|
579 |
$default = null;
|
|
|
580 |
|
|
|
581 |
if ($xmldb_field->getDefault() !== NULL) {
|
|
|
582 |
if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
|
|
|
583 |
$xmldb_field->getType() == XMLDB_TYPE_TEXT) {
|
|
|
584 |
if ($xmldb_field->getDefault() === '') { // If passing empty default, use the $default_for_char one instead
|
|
|
585 |
$default = "'" . $this->default_for_char . "'";
|
|
|
586 |
} else {
|
|
|
587 |
$default = "'" . $this->addslashes($xmldb_field->getDefault()) . "'";
|
|
|
588 |
}
|
|
|
589 |
} else {
|
|
|
590 |
$default = $xmldb_field->getDefault();
|
|
|
591 |
}
|
|
|
592 |
} else {
|
|
|
593 |
// We force default '' for not null char columns without proper default
|
|
|
594 |
// some day this should be out!
|
|
|
595 |
if ($this->default_for_char !== NULL &&
|
|
|
596 |
$xmldb_field->getType() == XMLDB_TYPE_CHAR &&
|
|
|
597 |
$xmldb_field->getNotNull()) {
|
|
|
598 |
$default = "'" . $this->default_for_char . "'";
|
|
|
599 |
} else {
|
|
|
600 |
// If the DB requires to explicity define some clause to drop one default, do it here
|
|
|
601 |
// never applying defaults to TEXT and BINARY fields
|
|
|
602 |
if ($this->drop_default_value_required &&
|
|
|
603 |
$xmldb_field->getType() != XMLDB_TYPE_TEXT &&
|
|
|
604 |
$xmldb_field->getType() != XMLDB_TYPE_BINARY && !$xmldb_field->getNotNull()) {
|
|
|
605 |
$default = $this->drop_default_value;
|
|
|
606 |
}
|
|
|
607 |
}
|
|
|
608 |
}
|
|
|
609 |
return $default;
|
|
|
610 |
}
|
|
|
611 |
|
|
|
612 |
/**
|
|
|
613 |
* Given one xmldb_field, returns the correct "default clause" for the current configuration.
|
|
|
614 |
*
|
|
|
615 |
* @param xmldb_field $xmldb_field The xmldb_field.
|
|
|
616 |
* @return The SQL clause for generating the default value as in $xmldb_field.
|
|
|
617 |
*/
|
|
|
618 |
public function getDefaultClause($xmldb_field) {
|
|
|
619 |
|
|
|
620 |
$defaultvalue = $this->getDefaultValue ($xmldb_field);
|
|
|
621 |
|
|
|
622 |
if ($defaultvalue !== null) {
|
|
|
623 |
return ' DEFAULT ' . $defaultvalue;
|
|
|
624 |
} else {
|
|
|
625 |
return null;
|
|
|
626 |
}
|
|
|
627 |
}
|
|
|
628 |
|
|
|
629 |
/**
|
|
|
630 |
* Given one correct xmldb_table and the new name, returns the SQL statements
|
|
|
631 |
* to rename it (inside one array).
|
|
|
632 |
*
|
|
|
633 |
* @param xmldb_table $xmldb_table The table to rename.
|
|
|
634 |
* @param string $newname The new name to rename the table to.
|
|
|
635 |
* @return array SQL statement(s) to rename the table.
|
|
|
636 |
*/
|
|
|
637 |
public function getRenameTableSQL($xmldb_table, $newname) {
|
|
|
638 |
|
|
|
639 |
$results = array(); //Array where all the sentences will be stored
|
|
|
640 |
|
|
|
641 |
$newt = new xmldb_table($newname); //Temporal table for name calculations
|
|
|
642 |
|
|
|
643 |
$rename = str_replace('OLDNAME', $this->getTableName($xmldb_table), $this->rename_table_sql);
|
|
|
644 |
$rename = str_replace('NEWNAME', $this->getTableName($newt), $rename);
|
|
|
645 |
|
|
|
646 |
$results[] = $rename;
|
|
|
647 |
|
|
|
648 |
// Call to getRenameTableExtraSQL() override if needed
|
|
|
649 |
$extra_sentences = $this->getRenameTableExtraSQL($xmldb_table, $newname);
|
|
|
650 |
$results = array_merge($results, $extra_sentences);
|
|
|
651 |
|
|
|
652 |
return $results;
|
|
|
653 |
}
|
|
|
654 |
|
|
|
655 |
/**
|
|
|
656 |
* Given one correct xmldb_table, returns the SQL statements
|
|
|
657 |
* to drop it (inside one array). Works also for temporary tables.
|
|
|
658 |
*
|
|
|
659 |
* @param xmldb_table $xmldb_table The table to drop.
|
|
|
660 |
* @return array SQL statement(s) for dropping the specified table.
|
|
|
661 |
*/
|
|
|
662 |
public function getDropTableSQL($xmldb_table) {
|
|
|
663 |
|
|
|
664 |
$results = array(); //Array where all the sentences will be stored
|
|
|
665 |
|
|
|
666 |
$drop = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_table_sql);
|
|
|
667 |
|
|
|
668 |
$results[] = $drop;
|
|
|
669 |
|
|
|
670 |
// call to getDropTableExtraSQL(), override if needed
|
|
|
671 |
$extra_sentences = $this->getDropTableExtraSQL($xmldb_table);
|
|
|
672 |
$results = array_merge($results, $extra_sentences);
|
|
|
673 |
|
|
|
674 |
return $results;
|
|
|
675 |
}
|
|
|
676 |
|
|
|
677 |
/**
|
|
|
678 |
* Performs any clean up that needs to be done after a table is dropped.
|
|
|
679 |
*
|
|
|
680 |
* @param xmldb_table $table
|
|
|
681 |
*/
|
|
|
682 |
public function cleanup_after_drop(xmldb_table $table): void {
|
|
|
683 |
if ($this->temptables->is_temptable($table->getName())) {
|
|
|
684 |
$this->temptables->delete_temptable($table->getName());
|
|
|
685 |
}
|
|
|
686 |
}
|
|
|
687 |
|
|
|
688 |
/**
|
|
|
689 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to add the field to the table.
|
|
|
690 |
*
|
|
|
691 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
692 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
|
693 |
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
|
694 |
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
|
695 |
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
|
696 |
* @return array The SQL statement for adding a field to the table.
|
|
|
697 |
*/
|
|
|
698 |
public function getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
|
|
|
699 |
|
|
|
700 |
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
|
701 |
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
|
702 |
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
|
703 |
|
|
|
704 |
$results = array();
|
|
|
705 |
|
|
|
706 |
// Get the quoted name of the table and field
|
|
|
707 |
$tablename = $this->getTableName($xmldb_table);
|
|
|
708 |
|
|
|
709 |
// Build the standard alter table add
|
|
|
710 |
$sql = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
|
|
|
711 |
$skip_default_clause,
|
|
|
712 |
$skip_notnull_clause);
|
|
|
713 |
$altertable = 'ALTER TABLE ' . $tablename . ' ADD ' . $sql;
|
|
|
714 |
// Add the after clause if necessary
|
|
|
715 |
if ($this->add_after_clause && $xmldb_field->getPrevious()) {
|
|
|
716 |
$altertable .= ' AFTER ' . $this->getEncQuoted($xmldb_field->getPrevious());
|
|
|
717 |
}
|
|
|
718 |
$results[] = $altertable;
|
|
|
719 |
|
|
|
720 |
return $results;
|
|
|
721 |
}
|
|
|
722 |
|
|
|
723 |
/**
|
|
|
724 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop the field from the table.
|
|
|
725 |
*
|
|
|
726 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
727 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
|
728 |
* @return array The SQL statement for dropping a field from the table.
|
|
|
729 |
*/
|
|
|
730 |
public function getDropFieldSQL($xmldb_table, $xmldb_field) {
|
|
|
731 |
|
|
|
732 |
$results = array();
|
|
|
733 |
|
|
|
734 |
// Get the quoted name of the table and field
|
|
|
735 |
$tablename = $this->getTableName($xmldb_table);
|
|
|
736 |
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
737 |
|
|
|
738 |
// Build the standard alter table drop
|
|
|
739 |
$results[] = 'ALTER TABLE ' . $tablename . ' DROP COLUMN ' . $fieldname;
|
|
|
740 |
|
|
|
741 |
return $results;
|
|
|
742 |
}
|
|
|
743 |
|
|
|
744 |
/**
|
|
|
745 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to alter the field in the table.
|
|
|
746 |
*
|
|
|
747 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
748 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
|
749 |
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
|
750 |
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
|
751 |
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
|
752 |
* @return array The field altering SQL statement.
|
|
|
753 |
*/
|
|
|
754 |
public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
|
|
|
755 |
|
|
|
756 |
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
|
757 |
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
|
758 |
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
|
759 |
|
|
|
760 |
$results = array();
|
|
|
761 |
|
|
|
762 |
// Get the quoted name of the table and field
|
|
|
763 |
$tablename = $this->getTableName($xmldb_table);
|
|
|
764 |
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
765 |
|
|
|
766 |
// Build de alter sentence using the alter_column_sql template
|
|
|
767 |
$alter = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->alter_column_sql);
|
|
|
768 |
$colspec = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
|
|
|
769 |
$skip_default_clause,
|
|
|
770 |
$skip_notnull_clause,
|
|
|
771 |
true);
|
|
|
772 |
$alter = str_replace('COLUMNSPECS', $colspec, $alter);
|
|
|
773 |
|
|
|
774 |
// Add the after clause if necessary
|
|
|
775 |
if ($this->add_after_clause && $xmldb_field->getPrevious()) {
|
|
|
776 |
$alter .= ' after ' . $this->getEncQuoted($xmldb_field->getPrevious());
|
|
|
777 |
}
|
|
|
778 |
|
|
|
779 |
// Build the standard alter table modify
|
|
|
780 |
$results[] = $alter;
|
|
|
781 |
|
|
|
782 |
return $results;
|
|
|
783 |
}
|
|
|
784 |
|
|
|
785 |
/**
|
|
|
786 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to modify the default of the field in the table.
|
|
|
787 |
*
|
|
|
788 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
789 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to get the modified default value from.
|
|
|
790 |
* @return array The SQL statement for modifying the default value.
|
|
|
791 |
*/
|
|
|
792 |
public function getModifyDefaultSQL($xmldb_table, $xmldb_field) {
|
|
|
793 |
|
|
|
794 |
$results = array();
|
|
|
795 |
|
|
|
796 |
// Get the quoted name of the table and field
|
|
|
797 |
$tablename = $this->getTableName($xmldb_table);
|
|
|
798 |
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
799 |
|
|
|
800 |
// Decide if we are going to create/modify or to drop the default
|
|
|
801 |
if ($xmldb_field->getDefault() === null) {
|
|
|
802 |
$results = $this->getDropDefaultSQL($xmldb_table, $xmldb_field); //Drop
|
|
|
803 |
} else {
|
|
|
804 |
$results = $this->getCreateDefaultSQL($xmldb_table, $xmldb_field); //Create/modify
|
|
|
805 |
}
|
|
|
806 |
|
|
|
807 |
return $results;
|
|
|
808 |
}
|
|
|
809 |
|
|
|
810 |
/**
|
|
|
811 |
* Given one correct xmldb_field and the new name, returns the SQL statements
|
|
|
812 |
* to rename it (inside one array).
|
|
|
813 |
*
|
|
|
814 |
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
|
815 |
* @param xmldb_field $xmldb_field The instance of xmldb_field to get the renamed field from.
|
|
|
816 |
* @param string $newname The new name to rename the field to.
|
|
|
817 |
* @return array The SQL statements for renaming the field.
|
|
|
818 |
*/
|
|
|
819 |
public function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
|
|
|
820 |
|
|
|
821 |
$results = array(); //Array where all the sentences will be stored
|
|
|
822 |
|
|
|
823 |
// Although this is checked in database_manager::rename_field() - double check
|
|
|
824 |
// that we aren't trying to rename one "id" field. Although it could be
|
|
|
825 |
// implemented (if adding the necessary code to rename sequences, defaults,
|
|
|
826 |
// triggers... and so on under each getRenameFieldExtraSQL() function, it's
|
|
|
827 |
// better to forbid it, mainly because this field is the default PK and
|
|
|
828 |
// in the future, a lot of FKs can be pointing here. So, this field, more
|
|
|
829 |
// or less, must be considered immutable!
|
|
|
830 |
if ($xmldb_field->getName() == 'id') {
|
|
|
831 |
return array();
|
|
|
832 |
}
|
|
|
833 |
|
|
|
834 |
$rename = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_column_sql);
|
|
|
835 |
$rename = str_replace('OLDFIELDNAME', $this->getEncQuoted($xmldb_field->getName()), $rename);
|
|
|
836 |
$rename = str_replace('NEWFIELDNAME', $this->getEncQuoted($newname), $rename);
|
|
|
837 |
|
|
|
838 |
$results[] = $rename;
|
|
|
839 |
|
|
|
840 |
// Call to getRenameFieldExtraSQL(), override if needed
|
|
|
841 |
$extra_sentences = $this->getRenameFieldExtraSQL($xmldb_table, $xmldb_field, $newname);
|
|
|
842 |
$results = array_merge($results, $extra_sentences);
|
|
|
843 |
|
|
|
844 |
return $results;
|
|
|
845 |
}
|
|
|
846 |
|
|
|
847 |
/**
|
|
|
848 |
* Given one xmldb_table and one xmldb_key, return the SQL statements needed to add the key to the table
|
|
|
849 |
* note that undelying indexes will be added as parametrised by $xxxx_keys and $xxxx_index parameters.
|
|
|
850 |
*
|
|
|
851 |
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
|
852 |
* @param xmldb_key $xmldb_key The xmldb_key to add.
|
|
|
853 |
* @return array SQL statement to add the xmldb_key.
|
|
|
854 |
*/
|
|
|
855 |
public function getAddKeySQL($xmldb_table, $xmldb_key) {
|
|
|
856 |
|
|
|
857 |
$results = array();
|
|
|
858 |
|
|
|
859 |
// Just use the CreateKeySQL function
|
|
|
860 |
if ($keyclause = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
|
861 |
$key = 'ALTER TABLE ' . $this->getTableName($xmldb_table) .
|
|
|
862 |
' ADD CONSTRAINT ' . $keyclause;
|
|
|
863 |
$results[] = $key;
|
|
|
864 |
}
|
|
|
865 |
|
|
|
866 |
// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
|
867 |
// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
|
|
|
868 |
if (!$keyclause || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
|
869 |
// Only if they don't exist
|
|
|
870 |
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN) { //Calculate type of index based on type ok key
|
|
|
871 |
$indextype = XMLDB_INDEX_NOTUNIQUE;
|
|
|
872 |
} else {
|
|
|
873 |
$indextype = XMLDB_INDEX_UNIQUE;
|
|
|
874 |
}
|
|
|
875 |
$xmldb_index = new xmldb_index('anyname', $indextype, $xmldb_key->getFields());
|
|
|
876 |
if (!$this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
|
|
|
877 |
$results = array_merge($results, $this->getAddIndexSQL($xmldb_table, $xmldb_index));
|
|
|
878 |
}
|
|
|
879 |
}
|
|
|
880 |
|
|
|
881 |
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
|
|
|
882 |
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
|
|
|
883 |
//Duplicate the key
|
|
|
884 |
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
|
885 |
$results = array_merge($results, $this->getAddKeySQL($xmldb_table, $xmldb_key));
|
|
|
886 |
}
|
|
|
887 |
|
|
|
888 |
// Return results
|
|
|
889 |
return $results;
|
|
|
890 |
}
|
|
|
891 |
|
|
|
892 |
/**
|
|
|
893 |
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
|
|
|
894 |
*
|
|
|
895 |
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
|
896 |
* @param xmldb_key $xmldb_key The xmldb_key to drop.
|
|
|
897 |
* @return array SQL statement to drop the xmldb_key.
|
|
|
898 |
*/
|
|
|
899 |
public function getDropKeySQL($xmldb_table, $xmldb_key) {
|
|
|
900 |
|
|
|
901 |
$results = array();
|
|
|
902 |
|
|
|
903 |
// Get the key name (note that this doesn't introspect DB, so could cause some problems sometimes!)
|
|
|
904 |
// TODO: We'll need to overwrite the whole getDropKeySQL() method inside each DB to do the proper queries
|
|
|
905 |
// against the dictionary or require ADOdb to support it or change the find_key_name() method to
|
|
|
906 |
// perform DB introspection directly. But, for now, as we aren't going to enable referential integrity
|
|
|
907 |
// it won't be a problem at all
|
|
|
908 |
$dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
|
|
|
909 |
|
|
|
910 |
// Only if such type of key generation is enabled
|
|
|
911 |
$dropkey = false;
|
|
|
912 |
switch ($xmldb_key->getType()) {
|
|
|
913 |
case XMLDB_KEY_PRIMARY:
|
|
|
914 |
if ($this->primary_keys) {
|
|
|
915 |
$template = $this->drop_primary_key;
|
|
|
916 |
$dropkey = true;
|
|
|
917 |
}
|
|
|
918 |
break;
|
|
|
919 |
case XMLDB_KEY_UNIQUE:
|
|
|
920 |
if ($this->unique_keys) {
|
|
|
921 |
$template = $this->drop_unique_key;
|
|
|
922 |
$dropkey = true;
|
|
|
923 |
}
|
|
|
924 |
break;
|
|
|
925 |
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
|
926 |
case XMLDB_KEY_FOREIGN:
|
|
|
927 |
if ($this->foreign_keys) {
|
|
|
928 |
$template = $this->drop_foreign_key;
|
|
|
929 |
$dropkey = true;
|
|
|
930 |
}
|
|
|
931 |
break;
|
|
|
932 |
}
|
|
|
933 |
// If we have decided to drop the key, let's do it
|
|
|
934 |
if ($dropkey) {
|
|
|
935 |
// Replace TABLENAME, CONSTRAINTTYPE and KEYNAME as needed
|
|
|
936 |
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $template);
|
|
|
937 |
$dropsql = str_replace('KEYNAME', $dbkeyname, $dropsql);
|
|
|
938 |
|
|
|
939 |
$results[] = $dropsql;
|
|
|
940 |
}
|
|
|
941 |
|
|
|
942 |
// If we aren't dropping the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
|
943 |
// automatically by the RDBMS) drop the underlying (created by us) index (if exists)
|
|
|
944 |
if (!$dropkey || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
|
945 |
// Only if they exist
|
|
|
946 |
$xmldb_index = new xmldb_index('anyname', XMLDB_INDEX_UNIQUE, $xmldb_key->getFields());
|
|
|
947 |
if ($this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
|
|
|
948 |
$results = array_merge($results, $this->getDropIndexSQL($xmldb_table, $xmldb_index));
|
|
|
949 |
}
|
|
|
950 |
}
|
|
|
951 |
|
|
|
952 |
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, drop the UNIQUE too
|
|
|
953 |
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
|
|
|
954 |
//Duplicate the key
|
|
|
955 |
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
|
956 |
$results = array_merge($results, $this->getDropKeySQL($xmldb_table, $xmldb_key));
|
|
|
957 |
}
|
|
|
958 |
|
|
|
959 |
// Return results
|
|
|
960 |
return $results;
|
|
|
961 |
}
|
|
|
962 |
|
|
|
963 |
/**
|
|
|
964 |
* Given one xmldb_table and one xmldb_key, return the SQL statements needed to rename the key in the table
|
|
|
965 |
* Experimental! Shouldn't be used at all!
|
|
|
966 |
*
|
|
|
967 |
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
|
968 |
* @param xmldb_key $xmldb_key The xmldb_key to rename.
|
|
|
969 |
* @param string $newname The xmldb_key's new name.
|
|
|
970 |
* @return array SQL statement to rename the xmldb_key.
|
|
|
971 |
*/
|
|
|
972 |
public function getRenameKeySQL($xmldb_table, $xmldb_key, $newname) {
|
|
|
973 |
|
|
|
974 |
$results = array();
|
|
|
975 |
|
|
|
976 |
// Get the real key name
|
|
|
977 |
$dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
|
|
|
978 |
|
|
|
979 |
// Check we are really generating this type of keys
|
|
|
980 |
if (($xmldb_key->getType() == XMLDB_KEY_PRIMARY && !$this->primary_keys) ||
|
|
|
981 |
($xmldb_key->getType() == XMLDB_KEY_UNIQUE && !$this->unique_keys) ||
|
|
|
982 |
($xmldb_key->getType() == XMLDB_KEY_FOREIGN && !$this->foreign_keys) ||
|
|
|
983 |
($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && !$this->unique_keys && !$this->foreign_keys)) {
|
|
|
984 |
// We aren't generating this type of keys, delegate to child indexes
|
|
|
985 |
$xmldb_index = new xmldb_index($xmldb_key->getName());
|
|
|
986 |
$xmldb_index->setFields($xmldb_key->getFields());
|
|
|
987 |
return $this->getRenameIndexSQL($xmldb_table, $xmldb_index, $newname);
|
|
|
988 |
}
|
|
|
989 |
|
|
|
990 |
// Arrived here so we are working with keys, lets rename them
|
|
|
991 |
// Replace TABLENAME and KEYNAME as needed
|
|
|
992 |
$renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_key_sql);
|
|
|
993 |
$renamesql = str_replace('OLDKEYNAME', $dbkeyname, $renamesql);
|
|
|
994 |
$renamesql = str_replace('NEWKEYNAME', $newname, $renamesql);
|
|
|
995 |
|
|
|
996 |
// Some DB doesn't support key renaming so this can be empty
|
|
|
997 |
if ($renamesql) {
|
|
|
998 |
$results[] = $renamesql;
|
|
|
999 |
}
|
|
|
1000 |
|
|
|
1001 |
return $results;
|
|
|
1002 |
}
|
|
|
1003 |
|
|
|
1004 |
/**
|
|
|
1005 |
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to add the index to the table.
|
|
|
1006 |
*
|
|
|
1007 |
* @param xmldb_table $xmldb_table The xmldb_table instance to add the index on.
|
|
|
1008 |
* @param xmldb_index $xmldb_index The xmldb_index to add.
|
|
|
1009 |
* @return array An array of SQL statements to add the index.
|
|
|
1010 |
*/
|
|
|
1011 |
public function getAddIndexSQL($xmldb_table, $xmldb_index) {
|
|
|
1012 |
|
|
|
1013 |
// Just use the CreateIndexSQL function
|
|
|
1014 |
return $this->getCreateIndexSQL($xmldb_table, $xmldb_index);
|
|
|
1015 |
}
|
|
|
1016 |
|
|
|
1017 |
/**
|
|
|
1018 |
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
|
|
|
1019 |
*
|
|
|
1020 |
* @param xmldb_table $xmldb_table The xmldb_table instance to drop the index on.
|
|
|
1021 |
* @param xmldb_index $xmldb_index The xmldb_index to drop.
|
|
|
1022 |
* @return array An array of SQL statements to drop the index.
|
|
|
1023 |
*/
|
|
|
1024 |
public function getDropIndexSQL($xmldb_table, $xmldb_index) {
|
|
|
1025 |
|
|
|
1026 |
$results = array();
|
|
|
1027 |
|
|
|
1028 |
// Get the real index name
|
|
|
1029 |
$dbindexnames = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index, true);
|
|
|
1030 |
|
|
|
1031 |
// Replace TABLENAME and INDEXNAME as needed
|
|
|
1032 |
if ($dbindexnames) {
|
|
|
1033 |
foreach ($dbindexnames as $dbindexname) {
|
|
|
1034 |
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
|
|
|
1035 |
$dropsql = str_replace('INDEXNAME', $this->getEncQuoted($dbindexname), $dropsql);
|
|
|
1036 |
$results[] = $dropsql;
|
|
|
1037 |
}
|
|
|
1038 |
}
|
|
|
1039 |
|
|
|
1040 |
return $results;
|
|
|
1041 |
}
|
|
|
1042 |
|
|
|
1043 |
/**
|
|
|
1044 |
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to rename the index in the table
|
|
|
1045 |
* Experimental! Shouldn't be used at all!
|
|
|
1046 |
*
|
|
|
1047 |
* @param xmldb_table $xmldb_table The xmldb_table instance to rename the index on.
|
|
|
1048 |
* @param xmldb_index $xmldb_index The xmldb_index to rename.
|
|
|
1049 |
* @param string $newname The xmldb_index's new name.
|
|
|
1050 |
* @return array An array of SQL statements to rename the index.
|
|
|
1051 |
*/
|
|
|
1052 |
function getRenameIndexSQL($xmldb_table, $xmldb_index, $newname) {
|
|
|
1053 |
// Some DB doesn't support index renaming (MySQL) so this can be empty
|
|
|
1054 |
if (empty($this->rename_index_sql)) {
|
|
|
1055 |
return array();
|
|
|
1056 |
}
|
|
|
1057 |
|
|
|
1058 |
// Get the real index name
|
|
|
1059 |
$dbindexname = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index);
|
|
|
1060 |
// Replace TABLENAME and INDEXNAME as needed
|
|
|
1061 |
$renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_index_sql);
|
|
|
1062 |
$renamesql = str_replace('OLDINDEXNAME', $this->getEncQuoted($dbindexname), $renamesql);
|
|
|
1063 |
$renamesql = str_replace('NEWINDEXNAME', $this->getEncQuoted($newname), $renamesql);
|
|
|
1064 |
|
|
|
1065 |
return array($renamesql);
|
|
|
1066 |
}
|
|
|
1067 |
|
|
|
1068 |
/**
|
|
|
1069 |
* Given three strings (table name, list of fields (comma separated) and suffix),
|
|
|
1070 |
* create the proper object name quoting it if necessary.
|
|
|
1071 |
*
|
|
|
1072 |
* IMPORTANT: This function must be used to CALCULATE NAMES of objects TO BE CREATED,
|
|
|
1073 |
* NEVER TO GUESS NAMES of EXISTING objects!!!
|
|
|
1074 |
*
|
|
|
1075 |
* @param string $tablename The table name.
|
|
|
1076 |
* @param string $fields A list of comma separated fields.
|
|
|
1077 |
* @param string $suffix A suffix for the object name.
|
|
|
1078 |
* @return string Object's name.
|
|
|
1079 |
*/
|
|
|
1080 |
public function getNameForObject($tablename, $fields, $suffix='') {
|
|
|
1081 |
|
|
|
1082 |
$name = '';
|
|
|
1083 |
|
|
|
1084 |
// Implement one basic cache to avoid object name duplication
|
|
|
1085 |
// along all the request life, but never to return cached results
|
|
|
1086 |
// We need this because sql statements are created before executing
|
|
|
1087 |
// them, hence names doesn't exist "physically" yet in DB, so we need
|
|
|
1088 |
// to known which ones have been used.
|
|
|
1089 |
// We track all the keys used, and the previous counters to make subsequent creates faster.
|
|
|
1090 |
// This may happen a lot with things like bulk backups or restores.
|
|
|
1091 |
static $usednames = array();
|
|
|
1092 |
static $previouscounters = array();
|
|
|
1093 |
|
|
|
1094 |
// Use standard naming. See http://docs.moodle.org/en/XMLDB_key_and_index_naming
|
|
|
1095 |
$tablearr = explode ('_', $tablename);
|
|
|
1096 |
foreach ($tablearr as $table) {
|
|
|
1097 |
$name .= substr(trim($table),0,4);
|
|
|
1098 |
}
|
|
|
1099 |
$name .= '_';
|
|
|
1100 |
$fieldsarr = explode (',', $fields);
|
|
|
1101 |
foreach ($fieldsarr as $field) {
|
|
|
1102 |
$name .= substr(trim($field),0,3);
|
|
|
1103 |
}
|
|
|
1104 |
// Prepend the prefix
|
|
|
1105 |
$name = trim($this->prefix . $name);
|
|
|
1106 |
|
|
|
1107 |
// Make sure name does not exceed the maximum name length and add suffix.
|
|
|
1108 |
$maxlengthwithoutsuffix = $this->names_max_length - strlen($suffix) - ($suffix ? 1 : 0);
|
|
|
1109 |
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix) . ($suffix ? ('_' . $suffix) : '');
|
|
|
1110 |
|
|
|
1111 |
if (isset($previouscounters[$name])) {
|
|
|
1112 |
// If we have a counter stored, we will need to modify the key to the next counter location.
|
|
|
1113 |
$counter = $previouscounters[$name] + 1;
|
|
|
1114 |
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
|
|
|
1115 |
$counter . ($suffix ? ('_' . $suffix) : '');
|
|
|
1116 |
} else {
|
|
|
1117 |
$counter = 1;
|
|
|
1118 |
}
|
|
|
1119 |
|
|
|
1120 |
// If the calculated name is in the cache, or if we detect it by introspecting the DB let's modify it.
|
|
|
1121 |
while (isset($usednames[$namewithsuffix]) || $this->isNameInUse($namewithsuffix, $suffix, $tablename)) {
|
|
|
1122 |
// Now iterate until not used name is found, incrementing the counter
|
|
|
1123 |
$counter++;
|
|
|
1124 |
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
|
|
|
1125 |
$counter . ($suffix ? ('_' . $suffix) : '');
|
|
|
1126 |
}
|
|
|
1127 |
|
|
|
1128 |
// Add the name to the cache. Using key look with isset because it is much faster than in_array.
|
|
|
1129 |
$usednames[$namewithsuffix] = true;
|
|
|
1130 |
$previouscounters[$name] = $counter;
|
|
|
1131 |
|
|
|
1132 |
// Quote it if necessary (reserved words)
|
|
|
1133 |
$namewithsuffix = $this->getEncQuoted($namewithsuffix);
|
|
|
1134 |
|
|
|
1135 |
return $namewithsuffix;
|
|
|
1136 |
}
|
|
|
1137 |
|
|
|
1138 |
/**
|
|
|
1139 |
* Given any string (or one array), enclose it by the proper quotes
|
|
|
1140 |
* if it's a reserved word
|
|
|
1141 |
*
|
|
|
1142 |
* @param string|array $input String to quote.
|
|
|
1143 |
* @return string|array Quoted string.
|
|
|
1144 |
*/
|
|
|
1145 |
public function getEncQuoted($input) {
|
|
|
1146 |
|
|
|
1147 |
if (is_array($input)) {
|
|
|
1148 |
foreach ($input as $key=>$content) {
|
|
|
1149 |
$input[$key] = $this->getEncQuoted($content);
|
|
|
1150 |
}
|
|
|
1151 |
return $input;
|
|
|
1152 |
} else {
|
|
|
1153 |
// Always lowercase
|
|
|
1154 |
$input = strtolower($input);
|
|
|
1155 |
// if reserved or quote_all or has hyphens, quote it
|
|
|
1156 |
if ($this->quote_all || in_array($input, $this->reserved_words) || strpos($input, '-') !== false) {
|
|
|
1157 |
$input = $this->quote_string . $input . $this->quote_string;
|
|
|
1158 |
}
|
|
|
1159 |
return $input;
|
|
|
1160 |
}
|
|
|
1161 |
}
|
|
|
1162 |
|
|
|
1163 |
/**
|
|
|
1164 |
* Given one XMLDB Statement, build the needed SQL insert sentences to execute it.
|
|
|
1165 |
*
|
|
|
1166 |
* @param mixed $statement SQL statement.
|
|
|
1167 |
* @return array Array of sentences in the SQL statement.
|
|
|
1168 |
*/
|
|
|
1169 |
function getExecuteInsertSQL($statement) {
|
|
|
1170 |
|
|
|
1171 |
$results = array(); //Array where all the sentences will be stored
|
|
|
1172 |
|
|
|
1173 |
if ($sentences = $statement->getSentences()) {
|
|
|
1174 |
foreach ($sentences as $sentence) {
|
|
|
1175 |
// Get the list of fields
|
|
|
1176 |
$fields = $statement->getFieldsFromInsertSentence($sentence);
|
|
|
1177 |
// Get the values of fields
|
|
|
1178 |
$values = $statement->getValuesFromInsertSentence($sentence);
|
|
|
1179 |
// Look if we have some CONCAT value and transform it dynamically
|
|
|
1180 |
foreach($values as $key => $value) {
|
|
|
1181 |
// Trim single quotes
|
|
|
1182 |
$value = trim($value,"'");
|
|
|
1183 |
if (stristr($value, 'CONCAT') !== false){
|
|
|
1184 |
// Look for data between parenthesis
|
|
|
1185 |
preg_match("/CONCAT\s*\((.*)\)$/is", trim($value), $matches);
|
|
|
1186 |
if (isset($matches[1])) {
|
|
|
1187 |
$part = $matches[1];
|
|
|
1188 |
// Convert the comma separated string to an array
|
|
|
1189 |
$arr = xmldb_object::comma2array($part);
|
|
|
1190 |
if ($arr) {
|
|
|
1191 |
$value = $this->getConcatSQL($arr);
|
|
|
1192 |
}
|
|
|
1193 |
}
|
|
|
1194 |
}
|
|
|
1195 |
// Values to be sent to DB must be properly escaped
|
|
|
1196 |
$value = $this->addslashes($value);
|
|
|
1197 |
// Back trimmed quotes
|
|
|
1198 |
$value = "'" . $value . "'";
|
|
|
1199 |
// Back to the array
|
|
|
1200 |
$values[$key] = $value;
|
|
|
1201 |
}
|
|
|
1202 |
|
|
|
1203 |
// Iterate over fields, escaping them if necessary
|
|
|
1204 |
foreach($fields as $key => $field) {
|
|
|
1205 |
$fields[$key] = $this->getEncQuoted($field);
|
|
|
1206 |
}
|
|
|
1207 |
// Build the final SQL sentence and add it to the array of results
|
|
|
1208 |
$sql = 'INSERT INTO ' . $this->getEncQuoted($this->prefix . $statement->getTable()) .
|
|
|
1209 |
'(' . implode(', ', $fields) . ') ' .
|
|
|
1210 |
'VALUES (' . implode(', ', $values) . ')';
|
|
|
1211 |
$results[] = $sql;
|
|
|
1212 |
}
|
|
|
1213 |
|
|
|
1214 |
}
|
|
|
1215 |
return $results;
|
|
|
1216 |
}
|
|
|
1217 |
|
|
|
1218 |
/**
|
|
|
1219 |
* Given one array of elements, build the proper CONCAT expression, based
|
|
|
1220 |
* in the $concat_character setting. If such setting is empty, then
|
|
|
1221 |
* MySQL's CONCAT function will be used instead.
|
|
|
1222 |
*
|
|
|
1223 |
* @param array $elements An array of elements to concatenate.
|
|
|
1224 |
* @return mixed Returns the result of moodle_database::sql_concat() or false.
|
|
|
1225 |
* @uses moodle_database::sql_concat()
|
|
|
1226 |
* @uses call_user_func_array()
|
|
|
1227 |
*/
|
|
|
1228 |
public function getConcatSQL($elements) {
|
|
|
1229 |
|
|
|
1230 |
// Replace double quoted elements by single quotes
|
|
|
1231 |
foreach($elements as $key => $element) {
|
|
|
1232 |
$element = trim($element);
|
|
|
1233 |
if (substr($element, 0, 1) == '"' &&
|
|
|
1234 |
substr($element, -1, 1) == '"') {
|
|
|
1235 |
$elements[$key] = "'" . trim($element, '"') . "'";
|
|
|
1236 |
}
|
|
|
1237 |
}
|
|
|
1238 |
|
|
|
1239 |
// Now call the standard $DB->sql_concat() DML function
|
|
|
1240 |
return call_user_func_array(array($this->mdb, 'sql_concat'), array_values($elements));
|
|
|
1241 |
}
|
|
|
1242 |
|
|
|
1243 |
/**
|
|
|
1244 |
* Returns the name (string) of the sequence used in the table for the autonumeric pk
|
|
|
1245 |
* Only some DB have this implemented.
|
|
|
1246 |
*
|
|
|
1247 |
* @param xmldb_table $xmldb_table The xmldb_table instance.
|
|
|
1248 |
* @return bool Returns the sequence from the DB or false.
|
|
|
1249 |
*/
|
|
|
1250 |
public function getSequenceFromDB($xmldb_table) {
|
|
|
1251 |
return false;
|
|
|
1252 |
}
|
|
|
1253 |
|
|
|
1254 |
/**
|
|
|
1255 |
* Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg).
|
|
|
1256 |
*
|
|
|
1257 |
* (MySQL requires the whole xmldb_table object to be specified, so we add it always)
|
|
|
1258 |
*
|
|
|
1259 |
* This is invoked from getNameForObject().
|
|
|
1260 |
* Only some DB have this implemented.
|
|
|
1261 |
*
|
|
|
1262 |
* @param string $object_name The object's name to check for.
|
|
|
1263 |
* @param string $type The object's type (pk, uk, fk, ck, ix, uix, seq, trg).
|
|
|
1264 |
* @param string $table_name The table's name to check in
|
|
|
1265 |
* @return bool If such name is currently in use (true) or no (false)
|
|
|
1266 |
*/
|
|
|
1267 |
public function isNameInUse($object_name, $type, $table_name) {
|
|
|
1268 |
return false; //For generators not implementing introspection,
|
|
|
1269 |
//we always return with the name being free to be used
|
|
|
1270 |
}
|
|
|
1271 |
|
|
|
1272 |
|
|
|
1273 |
// ====== FOLLOWING FUNCTION MUST BE CUSTOMISED BY ALL THE XMLDGenerator classes ========
|
|
|
1274 |
|
|
|
1275 |
/**
|
|
|
1276 |
* Reset a sequence to the id field of a table.
|
|
|
1277 |
*
|
|
|
1278 |
* @param xmldb_table|string $table name of table or the table object.
|
|
|
1279 |
* @return array of sql statements
|
|
|
1280 |
*/
|
|
|
1281 |
abstract public function getResetSequenceSQL($table);
|
|
|
1282 |
|
|
|
1283 |
/**
|
|
|
1284 |
* Given one correct xmldb_table, returns the SQL statements
|
|
|
1285 |
* to create temporary table (inside one array).
|
|
|
1286 |
*
|
|
|
1287 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1288 |
* @return array of sql statements
|
|
|
1289 |
*/
|
|
|
1290 |
abstract public function getCreateTempTableSQL($xmldb_table);
|
|
|
1291 |
|
|
|
1292 |
/**
|
|
|
1293 |
* Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
|
|
|
1294 |
*
|
|
|
1295 |
* @param int $xmldb_type The xmldb_type defined constant. XMLDB_TYPE_INTEGER and other XMLDB_TYPE_* constants.
|
|
|
1296 |
* @param int $xmldb_length The length of that data type.
|
|
|
1297 |
* @param int $xmldb_decimals The decimal places of precision of the data type.
|
|
|
1298 |
* @return string The DB defined data type.
|
|
|
1299 |
*/
|
|
|
1300 |
abstract public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null);
|
|
|
1301 |
|
|
|
1302 |
/**
|
|
|
1303 |
* Returns the code (array of statements) needed to execute extra statements on field rename.
|
|
|
1304 |
*
|
|
|
1305 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1306 |
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
|
1307 |
* @return array Array of extra SQL statements to run with a field being renamed.
|
|
|
1308 |
*/
|
|
|
1309 |
public function getRenameFieldExtraSQL($xmldb_table, $xmldb_field) {
|
|
|
1310 |
return array();
|
|
|
1311 |
}
|
|
|
1312 |
|
|
|
1313 |
/**
|
|
|
1314 |
* Returns the code (array of statements) needed
|
|
|
1315 |
* to create one sequence for the xmldb_table and xmldb_field passed in.
|
|
|
1316 |
*
|
|
|
1317 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1318 |
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
|
1319 |
* @return array Array of SQL statements to create the sequence.
|
|
|
1320 |
*/
|
|
|
1321 |
public function getCreateSequenceSQL($xmldb_table, $xmldb_field) {
|
|
|
1322 |
return array();
|
|
|
1323 |
}
|
|
|
1324 |
|
|
|
1325 |
/**
|
|
|
1326 |
* Returns the code (array of statements) needed to add one comment to the table.
|
|
|
1327 |
*
|
|
|
1328 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1329 |
* @return array Array of SQL statements to add one comment to the table.
|
|
|
1330 |
*/
|
|
|
1331 |
abstract public function getCommentSQL($xmldb_table);
|
|
|
1332 |
|
|
|
1333 |
/**
|
|
|
1334 |
* Returns the code (array of statements) needed to execute extra statements on table rename.
|
|
|
1335 |
*
|
|
|
1336 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1337 |
* @param string $newname The new name for the table.
|
|
|
1338 |
* @return array Array of extra SQL statements to rename a table.
|
|
|
1339 |
*/
|
|
|
1340 |
public function getRenameTableExtraSQL($xmldb_table, $newname) {
|
|
|
1341 |
return array();
|
|
|
1342 |
}
|
|
|
1343 |
|
|
|
1344 |
/**
|
|
|
1345 |
* Returns the code (array of statements) needed to execute extra statements on table drop
|
|
|
1346 |
*
|
|
|
1347 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1348 |
* @return array Array of extra SQL statements to drop a table.
|
|
|
1349 |
*/
|
|
|
1350 |
public function getDropTableExtraSQL($xmldb_table) {
|
|
|
1351 |
return array();
|
|
|
1352 |
}
|
|
|
1353 |
|
|
|
1354 |
/**
|
|
|
1355 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop its default
|
|
|
1356 |
* (usually invoked from getModifyDefaultSQL()
|
|
|
1357 |
*
|
|
|
1358 |
* Note that this method may be dropped in future.
|
|
|
1359 |
*
|
|
|
1360 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1361 |
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
|
1362 |
* @return array Array of SQL statements to create a field's default.
|
|
|
1363 |
*
|
|
|
1364 |
* @todo MDL-31147 Moodle 2.1 - Drop getDropDefaultSQL()
|
|
|
1365 |
*/
|
|
|
1366 |
abstract public function getDropDefaultSQL($xmldb_table, $xmldb_field);
|
|
|
1367 |
|
|
|
1368 |
/**
|
|
|
1369 |
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to add its default
|
|
|
1370 |
* (usually invoked from getModifyDefaultSQL()
|
|
|
1371 |
*
|
|
|
1372 |
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
|
1373 |
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
|
1374 |
* @return array Array of SQL statements to create a field's default.
|
|
|
1375 |
*/
|
|
|
1376 |
abstract public function getCreateDefaultSQL($xmldb_table, $xmldb_field);
|
|
|
1377 |
|
|
|
1378 |
/**
|
|
|
1379 |
* Returns an array of reserved words (lowercase) for this DB
|
|
|
1380 |
* You MUST provide the real list for each DB inside every XMLDB class.
|
|
|
1381 |
* @return array An array of database specific reserved words.
|
|
|
1382 |
* @throws coding_exception Thrown if not implemented for the specific DB.
|
|
|
1383 |
*/
|
|
|
1384 |
public static function getReservedWords() {
|
|
|
1385 |
throw new coding_exception('getReservedWords() method needs to be overridden in each subclass of sql_generator');
|
|
|
1386 |
}
|
|
|
1387 |
|
|
|
1388 |
/**
|
|
|
1389 |
* Returns all reserved words in supported databases.
|
|
|
1390 |
* Reserved words should be lowercase.
|
|
|
1391 |
* @return array ('word'=>array(databases))
|
|
|
1392 |
*/
|
|
|
1393 |
public static function getAllReservedWords() {
|
|
|
1394 |
global $CFG;
|
|
|
1395 |
|
|
|
1396 |
$generators = array('mysql', 'postgres', 'oracle', 'mssql');
|
|
|
1397 |
$reserved_words = array();
|
|
|
1398 |
|
|
|
1399 |
foreach($generators as $generator) {
|
|
|
1400 |
$class = $generator . '_sql_generator';
|
|
|
1401 |
require_once("$CFG->libdir/ddl/$class.php");
|
|
|
1402 |
foreach (call_user_func(array($class, 'getReservedWords')) as $word) {
|
|
|
1403 |
$reserved_words[$word][] = $generator;
|
|
|
1404 |
}
|
|
|
1405 |
}
|
|
|
1406 |
ksort($reserved_words);
|
|
|
1407 |
return $reserved_words;
|
|
|
1408 |
}
|
|
|
1409 |
|
|
|
1410 |
/**
|
|
|
1411 |
* Adds slashes to string.
|
|
|
1412 |
* @param string $s
|
|
|
1413 |
* @return string The escaped string.
|
|
|
1414 |
*/
|
|
|
1415 |
public function addslashes($s) {
|
|
|
1416 |
// do not use php addslashes() because it depends on PHP quote settings!
|
|
|
1417 |
$s = str_replace('\\','\\\\',$s);
|
|
|
1418 |
$s = str_replace("\0","\\\0", $s);
|
|
|
1419 |
$s = str_replace("'", "\\'", $s);
|
|
|
1420 |
return $s;
|
|
|
1421 |
}
|
|
|
1422 |
|
|
|
1423 |
/**
|
|
|
1424 |
* Get the fields from an index definition that might be null.
|
|
|
1425 |
* @param xmldb_table $xmldb_table the table
|
|
|
1426 |
* @param xmldb_index $xmldb_index the index
|
|
|
1427 |
* @return array list of fields in the index definition that might be null.
|
|
|
1428 |
*/
|
|
|
1429 |
public function get_nullable_fields_in_index($xmldb_table, $xmldb_index) {
|
|
|
1430 |
global $DB;
|
|
|
1431 |
|
|
|
1432 |
// If we don't have the field info passed in, we need to query it from the DB.
|
|
|
1433 |
$fieldsfromdb = null;
|
|
|
1434 |
|
|
|
1435 |
$nullablefields = [];
|
|
|
1436 |
foreach ($xmldb_index->getFields() as $fieldname) {
|
|
|
1437 |
if ($field = $xmldb_table->getField($fieldname)) {
|
|
|
1438 |
// We have the field details in the table definition.
|
|
|
1439 |
if ($field->getNotNull() !== XMLDB_NOTNULL) {
|
|
|
1440 |
$nullablefields[] = $fieldname;
|
|
|
1441 |
}
|
|
|
1442 |
|
|
|
1443 |
} else {
|
|
|
1444 |
// We don't have the table definition loaded. Need to
|
|
|
1445 |
// inspect the database.
|
|
|
1446 |
if ($fieldsfromdb === null) {
|
|
|
1447 |
$fieldsfromdb = $DB->get_columns($xmldb_table->getName(), false);
|
|
|
1448 |
}
|
|
|
1449 |
if (!isset($fieldsfromdb[$fieldname])) {
|
|
|
1450 |
throw new coding_exception('Unknown field ' . $fieldname .
|
|
|
1451 |
' in index ' . $xmldb_index->getName());
|
|
|
1452 |
}
|
|
|
1453 |
|
|
|
1454 |
if (!$fieldsfromdb[$fieldname]->not_null) {
|
|
|
1455 |
$nullablefields[] = $fieldname;
|
|
|
1456 |
}
|
|
|
1457 |
}
|
|
|
1458 |
}
|
|
|
1459 |
|
|
|
1460 |
return $nullablefields;
|
|
|
1461 |
}
|
|
|
1462 |
}
|