PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ddl/sql_generator.php

https://bitbucket.org/moodle/moodle
PHP | 1462 lines | 712 code | 205 blank | 545 comment | 143 complexity | ff6a44119570f2a168f710d1bbdda021 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * This class represent the base generator class where all the needed functions to generate proper SQL are defined.
  18. *
  19. * The rest of classes will inherit, by default, the same logic.
  20. * Functions will be overridden as needed to generate correct SQL.
  21. *
  22. * @package core_ddl
  23. * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
  24. * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  25. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26. */
  27. defined('MOODLE_INTERNAL') || die();
  28. /**
  29. * Abstract sql generator class, base for all db specific implementations.
  30. *
  31. * @package core_ddl
  32. * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
  33. * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  34. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35. */
  36. abstract class sql_generator {
  37. // Please, avoid editing this defaults in this base class!
  38. // It could change the behaviour of the rest of generators
  39. // that, by default, inherit this configuration.
  40. // To change any of them, do it in extended classes instead.
  41. /** @var string Used to quote names. */
  42. public $quote_string = '"';
  43. /** @var string To be automatically added at the end of each statement. */
  44. public $statement_end = ';';
  45. /** @var bool To decide if we want to quote all the names or only the reserved ones. */
  46. public $quote_all = false;
  47. /** @var bool To create all the integers as NUMBER(x) (also called DECIMAL, NUMERIC...). */
  48. public $integer_to_number = false;
  49. /** @var bool To create all the floats as NUMBER(x) (also called DECIMAL, NUMERIC...). */
  50. public $float_to_number = false;
  51. /** @var string Proper type for NUMBER(x) in this DB. */
  52. public $number_type = 'NUMERIC';
  53. /** @var string To define the default to set for NOT NULLs CHARs without default (null=do nothing).*/
  54. public $default_for_char = null;
  55. /** @var bool To specify if the generator must use some DEFAULT clause to drop defaults.*/
  56. public $drop_default_value_required = false;
  57. /** @var string The DEFAULT clause required to drop defaults.*/
  58. public $drop_default_value = '';
  59. /** @var bool To decide if the default clause of each field must go after the null clause.*/
  60. public $default_after_null = true;
  61. /** @var bool To force the generator if NULL clauses must be specified. It shouldn't be necessary.*/
  62. public $specify_nulls = false;
  63. /** @var string To force primary key names to one string (null=no force).*/
  64. public $primary_key_name = null;
  65. /** @var bool True if the generator builds primary keys.*/
  66. public $primary_keys = true;
  67. /** @var bool True if the generator builds unique keys.*/
  68. public $unique_keys = false;
  69. /** @var bool True if the generator builds foreign keys.*/
  70. public $foreign_keys = false;
  71. /** @var string Template to drop PKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  72. public $drop_primary_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
  73. /** @var string Template to drop UKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  74. public $drop_unique_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
  75. /** @var string Template to drop FKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  76. public $drop_foreign_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
  77. /** @var bool True if the generator needs to add extra code to generate the sequence fields.*/
  78. public $sequence_extra_code = true;
  79. /** @var string The particular name for inline sequences in this generator.*/
  80. public $sequence_name = 'auto_increment';
  81. /** @var string|bool Different name for small (4byte) sequences or false if same.*/
  82. public $sequence_name_small = false;
  83. /**
  84. * @var bool To avoid outputting the rest of the field specs, leaving only the name and the sequence_name returned.
  85. * @see getFieldSQL()
  86. */
  87. public $sequence_only = false;
  88. /** @var bool True if the generator needs to add code for table comments.*/
  89. public $add_table_comments = true;
  90. /** @var bool True if the generator needs to add the after clause for fields.*/
  91. public $add_after_clause = false;
  92. /**
  93. * @var bool True if the generator needs to prepend the prefix to all the key/index/sequence/trigger/check names.
  94. * @see $prefix
  95. */
  96. public $prefix_on_names = true;
  97. /** @var int Maximum length for key/index/sequence/trigger/check names (keep 30 for all!).*/
  98. public $names_max_length = 30;
  99. /** @var string Characters to be used as concatenation operator. If not defined, MySQL CONCAT function will be used.*/
  100. public $concat_character = '||';
  101. /** @var string SQL sentence to rename one table, both 'OLDNAME' and 'NEWNAME' keywords are dynamically replaced.*/
  102. public $rename_table_sql = 'ALTER TABLE OLDNAME RENAME TO NEWNAME';
  103. /** @var string SQL sentence to drop one table where the 'TABLENAME' keyword is dynamically replaced.*/
  104. public $drop_table_sql = 'DROP TABLE TABLENAME';
  105. /** @var string The SQL template to alter columns where the 'TABLENAME' and 'COLUMNSPECS' keywords are dynamically replaced.*/
  106. public $alter_column_sql = 'ALTER TABLE TABLENAME ALTER COLUMN COLUMNSPECS';
  107. /** @var bool The generator will skip the default clause on alter columns.*/
  108. public $alter_column_skip_default = false;
  109. /** @var bool The generator will skip the type clause on alter columns.*/
  110. public $alter_column_skip_type = false;
  111. /** @var bool The generator will skip the null/notnull clause on alter columns.*/
  112. public $alter_column_skip_notnull = false;
  113. /** @var string SQL sentence to rename one column where 'TABLENAME', 'OLDFIELDNAME' and 'NEWFIELDNAME' keywords are dynamically replaced.*/
  114. public $rename_column_sql = 'ALTER TABLE TABLENAME RENAME COLUMN OLDFIELDNAME TO NEWFIELDNAME';
  115. /** @var string SQL sentence to drop one index where 'TABLENAME', 'INDEXNAME' keywords are dynamically replaced.*/
  116. public $drop_index_sql = 'DROP INDEX INDEXNAME';
  117. /** @var string SQL sentence to rename one index where 'TABLENAME', 'OLDINDEXNAME' and 'NEWINDEXNAME' are dynamically replaced.*/
  118. public $rename_index_sql = 'ALTER INDEX OLDINDEXNAME RENAME TO NEWINDEXNAME';
  119. /** @var string SQL sentence to rename one key 'TABLENAME', 'OLDKEYNAME' and 'NEWKEYNAME' are dynamically replaced.*/
  120. public $rename_key_sql = 'ALTER TABLE TABLENAME CONSTRAINT OLDKEYNAME RENAME TO NEWKEYNAME';
  121. /** @var string The prefix to be used for all the DB objects.*/
  122. public $prefix;
  123. /** @var string List of reserved words (in order to quote them properly).*/
  124. public $reserved_words;
  125. /** @var moodle_database The moodle_database instance.*/
  126. public $mdb;
  127. /** @var Control existing temptables.*/
  128. protected $temptables;
  129. /**
  130. * Creates a new sql_generator.
  131. * @param moodle_database $mdb The moodle_database object instance.
  132. * @param moodle_temptables $temptables The optional moodle_temptables instance, null by default.
  133. */
  134. public function __construct($mdb, $temptables = null) {
  135. $this->prefix = $mdb->get_prefix();
  136. $this->reserved_words = $this->getReservedWords();
  137. $this->mdb = $mdb; // this creates circular reference - the other link must be unset when closing db
  138. $this->temptables = $temptables;
  139. }
  140. /**
  141. * Releases all resources.
  142. */
  143. public function dispose() {
  144. $this->mdb = null;
  145. }
  146. /**
  147. * Given one string (or one array), ends it with $statement_end .
  148. *
  149. * @see $statement_end
  150. *
  151. * @param array|string $input SQL statement(s).
  152. * @return array|string
  153. */
  154. public function getEndedStatements($input) {
  155. if (is_array($input)) {
  156. foreach ($input as $key=>$content) {
  157. $input[$key] = $this->getEndedStatements($content);
  158. }
  159. return $input;
  160. } else {
  161. $input = trim($input).$this->statement_end;
  162. return $input;
  163. }
  164. }
  165. /**
  166. * Given one xmldb_table, checks if it exists in DB (true/false).
  167. *
  168. * @param mixed $table The table to be searched (string name or xmldb_table instance).
  169. * @return boolean true/false
  170. */
  171. public function table_exists($table) {
  172. if (is_string($table)) {
  173. $tablename = $table;
  174. } else {
  175. // Calculate the name of the table
  176. $tablename = $table->getName();
  177. }
  178. if ($this->temptables->is_temptable($tablename)) {
  179. return true;
  180. }
  181. // Get all tables in moodle database.
  182. $tables = $this->mdb->get_tables();
  183. return isset($tables[$tablename]);
  184. }
  185. /**
  186. * This function will return the SQL code needed to create db tables and statements.
  187. * @see xmldb_structure
  188. *
  189. * @param xmldb_structure $xmldb_structure An xmldb_structure instance.
  190. * @return array
  191. */
  192. public function getCreateStructureSQL($xmldb_structure) {
  193. $results = array();
  194. if ($tables = $xmldb_structure->getTables()) {
  195. foreach ($tables as $table) {
  196. $results = array_merge($results, $this->getCreateTableSQL($table));
  197. }
  198. }
  199. return $results;
  200. }
  201. /**
  202. * Given one xmldb_table, this returns it's correct name, depending of all the parameterization.
  203. * eg: This appends $prefix to the table name.
  204. *
  205. * @see $prefix
  206. *
  207. * @param xmldb_table $xmldb_table The table whose name we want.
  208. * @param boolean $quoted To specify if the name must be quoted (if reserved word, only!).
  209. * @return string The correct name of the table.
  210. */
  211. public function getTableName(xmldb_table $xmldb_table, $quoted=true) {
  212. // Get the name
  213. $tablename = $this->prefix.$xmldb_table->getName();
  214. // Apply quotes optionally
  215. if ($quoted) {
  216. $tablename = $this->getEncQuoted($tablename);
  217. }
  218. return $tablename;
  219. }
  220. /**
  221. * Given one correct xmldb_table, returns the SQL statements
  222. * to create it (inside one array).
  223. *
  224. * @param xmldb_table $xmldb_table An xmldb_table instance.
  225. * @return array An array of SQL statements, starting with the table creation SQL followed
  226. * by any of its comments, indexes and sequence creation SQL statements.
  227. */
  228. public function getCreateTableSQL($xmldb_table) {
  229. if ($error = $xmldb_table->validateDefinition()) {
  230. throw new coding_exception($error);
  231. }
  232. $results = array(); //Array where all the sentences will be stored
  233. // Table header
  234. $table = 'CREATE TABLE ' . $this->getTableName($xmldb_table) . ' (';
  235. if (!$xmldb_fields = $xmldb_table->getFields()) {
  236. return $results;
  237. }
  238. $sequencefield = null;
  239. // Add the fields, separated by commas
  240. foreach ($xmldb_fields as $xmldb_field) {
  241. if ($xmldb_field->getSequence()) {
  242. $sequencefield = $xmldb_field->getName();
  243. }
  244. $table .= "\n " . $this->getFieldSQL($xmldb_table, $xmldb_field);
  245. $table .= ',';
  246. }
  247. // Add the keys, separated by commas
  248. if ($xmldb_keys = $xmldb_table->getKeys()) {
  249. foreach ($xmldb_keys as $xmldb_key) {
  250. if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
  251. $table .= "\nCONSTRAINT " . $keytext . ',';
  252. }
  253. // If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
  254. if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
  255. //Duplicate the key
  256. $xmldb_key->setType(XMLDB_KEY_UNIQUE);
  257. if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
  258. $table .= "\nCONSTRAINT " . $keytext . ',';
  259. }
  260. }
  261. // make sure sequence field is unique
  262. if ($sequencefield and $xmldb_key->getType() == XMLDB_KEY_PRIMARY) {
  263. $fields = $xmldb_key->getFields();
  264. $field = reset($fields);
  265. if ($sequencefield === $field) {
  266. $sequencefield = null;
  267. }
  268. }
  269. }
  270. }
  271. // throw error if sequence field does not have unique key defined
  272. if ($sequencefield) {
  273. throw new ddl_exception('ddsequenceerror', $xmldb_table->getName());
  274. }
  275. // Table footer, trim the latest comma
  276. $table = trim($table,',');
  277. $table .= "\n)";
  278. // Add the CREATE TABLE to results
  279. $results[] = $table;
  280. // Add comments if specified and it exists
  281. if ($this->add_table_comments && $xmldb_table->getComment()) {
  282. $comment = $this->getCommentSQL($xmldb_table);
  283. // Add the COMMENT to results
  284. $results = array_merge($results, $comment);
  285. }
  286. // Add the indexes (each one, one statement)
  287. if ($xmldb_indexes = $xmldb_table->getIndexes()) {
  288. foreach ($xmldb_indexes as $xmldb_index) {
  289. //tables do not exist yet, which means indexed can not exist yet
  290. if ($indextext = $this->getCreateIndexSQL($xmldb_table, $xmldb_index)) {
  291. $results = array_merge($results, $indextext);
  292. }
  293. }
  294. }
  295. // Also, add the indexes needed from keys, based on configuration (each one, one statement)
  296. if ($xmldb_keys = $xmldb_table->getKeys()) {
  297. foreach ($xmldb_keys as $xmldb_key) {
  298. // If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
  299. // automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
  300. if (!$this->getKeySQL($xmldb_table, $xmldb_key) || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
  301. // Create the interim index
  302. $index = new xmldb_index('anyname');
  303. $index->setFields($xmldb_key->getFields());
  304. //tables do not exist yet, which means indexed can not exist yet
  305. $createindex = false; //By default
  306. switch ($xmldb_key->getType()) {
  307. case XMLDB_KEY_UNIQUE:
  308. case XMLDB_KEY_FOREIGN_UNIQUE:
  309. $index->setUnique(true);
  310. $createindex = true;
  311. break;
  312. case XMLDB_KEY_FOREIGN:
  313. $index->setUnique(false);
  314. $createindex = true;
  315. break;
  316. }
  317. if ($createindex) {
  318. if ($indextext = $this->getCreateIndexSQL($xmldb_table, $index)) {
  319. // Add the INDEX to the array
  320. $results = array_merge($results, $indextext);
  321. }
  322. }
  323. }
  324. }
  325. }
  326. // Add sequence extra code if needed
  327. if ($this->sequence_extra_code) {
  328. // Iterate over fields looking for sequences
  329. foreach ($xmldb_fields as $xmldb_field) {
  330. if ($xmldb_field->getSequence()) {
  331. // returns an array of statements needed to create one sequence
  332. $sequence_sentences = $this->getCreateSequenceSQL($xmldb_table, $xmldb_field);
  333. // Add the SEQUENCE to the array
  334. $results = array_merge($results, $sequence_sentences);
  335. }
  336. }
  337. }
  338. return $results;
  339. }
  340. /**
  341. * Given one correct xmldb_index, returns the SQL statements
  342. * needed to create it (in array).
  343. *
  344. * @param xmldb_table $xmldb_table The xmldb_table instance to create the index on.
  345. * @param xmldb_index $xmldb_index The xmldb_index to create.
  346. * @return array An array of SQL statements to create the index.
  347. * @throws coding_exception Thrown if the xmldb_index does not validate with the xmldb_table.
  348. */
  349. public function getCreateIndexSQL($xmldb_table, $xmldb_index) {
  350. if ($error = $xmldb_index->validateDefinition($xmldb_table)) {
  351. throw new coding_exception($error);
  352. }
  353. $unique = '';
  354. $suffix = 'ix';
  355. if ($xmldb_index->getUnique()) {
  356. $unique = ' UNIQUE';
  357. $suffix = 'uix';
  358. }
  359. $index = 'CREATE' . $unique . ' INDEX ';
  360. $index .= $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_index->getFields()), $suffix);
  361. $index .= ' ON ' . $this->getTableName($xmldb_table);
  362. $index .= ' (' . implode(', ', $this->getEncQuoted($xmldb_index->getFields())) . ')';
  363. return array($index);
  364. }
  365. /**
  366. * Given one correct xmldb_field, returns the complete SQL line to create it.
  367. *
  368. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  369. * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
  370. * @param string $skip_type_clause The type clause on alter columns, NULL by default.
  371. * @param string $skip_default_clause The default clause on alter columns, NULL by default.
  372. * @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
  373. * @param string $specify_nulls_clause To force a specific null clause, NULL by default.
  374. * @param bool $specify_field_name Flag to specify fieldname in return.
  375. * @return string The field generating SQL statement.
  376. * @throws coding_exception Thrown when xmldb_field doesn't validate with the xmldb_table.
  377. */
  378. 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) {
  379. if ($error = $xmldb_field->validateDefinition($xmldb_table)) {
  380. throw new coding_exception($error);
  381. }
  382. $skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
  383. $skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
  384. $skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
  385. $specify_nulls_clause = is_null($specify_nulls_clause) ? $this->specify_nulls : $specify_nulls_clause;
  386. // First of all, convert integers to numbers if defined
  387. if ($this->integer_to_number) {
  388. if ($xmldb_field->getType() == XMLDB_TYPE_INTEGER) {
  389. $xmldb_field->setType(XMLDB_TYPE_NUMBER);
  390. }
  391. }
  392. // Same for floats
  393. if ($this->float_to_number) {
  394. if ($xmldb_field->getType() == XMLDB_TYPE_FLOAT) {
  395. $xmldb_field->setType(XMLDB_TYPE_NUMBER);
  396. }
  397. }
  398. $field = ''; // Let's accumulate the whole expression based on params and settings
  399. // The name
  400. if ($specify_field_name) {
  401. $field .= $this->getEncQuoted($xmldb_field->getName());
  402. }
  403. // The type and length only if we don't want to skip it
  404. if (!$skip_type_clause) {
  405. // The type and length
  406. $field .= ' ' . $this->getTypeSQL($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
  407. }
  408. // note: unsigned is not supported any more since moodle 2.3, all numbers are signed
  409. // Calculate the not null clause
  410. $notnull = '';
  411. // Only if we don't want to skip it
  412. if (!$skip_notnull_clause) {
  413. if ($xmldb_field->getNotNull()) {
  414. $notnull = ' NOT NULL';
  415. } else {
  416. if ($specify_nulls_clause) {
  417. $notnull = ' NULL';
  418. }
  419. }
  420. }
  421. // Calculate the default clause
  422. $default_clause = '';
  423. if (!$skip_default_clause) { //Only if we don't want to skip it
  424. $default_clause = $this->getDefaultClause($xmldb_field);
  425. }
  426. // Based on default_after_null, set both clauses properly
  427. if ($this->default_after_null) {
  428. $field .= $notnull . $default_clause;
  429. } else {
  430. $field .= $default_clause . $notnull;
  431. }
  432. // The sequence
  433. if ($xmldb_field->getSequence()) {
  434. if($xmldb_field->getLength()<=9 && $this->sequence_name_small) {
  435. $sequencename=$this->sequence_name_small;
  436. } else {
  437. $sequencename=$this->sequence_name;
  438. }
  439. $field .= ' ' . $sequencename;
  440. if ($this->sequence_only) {
  441. // We only want the field name and sequence name to be printed
  442. // so, calculate it and return
  443. $sql = $this->getEncQuoted($xmldb_field->getName()) . ' ' . $sequencename;
  444. return $sql;
  445. }
  446. }
  447. return $field;
  448. }
  449. /**
  450. * Given one correct xmldb_key, returns its specs.
  451. *
  452. * @param xmldb_table $xmldb_table The table related to $xmldb_key.
  453. * @param xmldb_key $xmldb_key The xmldb_key's specifications requested.
  454. * @return string SQL statement about the xmldb_key.
  455. */
  456. public function getKeySQL($xmldb_table, $xmldb_key) {
  457. $key = '';
  458. switch ($xmldb_key->getType()) {
  459. case XMLDB_KEY_PRIMARY:
  460. if ($this->primary_keys) {
  461. if ($this->primary_key_name !== null) {
  462. $key = $this->getEncQuoted($this->primary_key_name);
  463. } else {
  464. $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'pk');
  465. }
  466. $key .= ' PRIMARY KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
  467. }
  468. break;
  469. case XMLDB_KEY_UNIQUE:
  470. if ($this->unique_keys) {
  471. $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'uk');
  472. $key .= ' UNIQUE (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
  473. }
  474. break;
  475. case XMLDB_KEY_FOREIGN:
  476. case XMLDB_KEY_FOREIGN_UNIQUE:
  477. if ($this->foreign_keys) {
  478. $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'fk');
  479. $key .= ' FOREIGN KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
  480. $key .= ' REFERENCES ' . $this->getEncQuoted($this->prefix . $xmldb_key->getRefTable());
  481. $key .= ' (' . implode(', ', $this->getEncQuoted($xmldb_key->getRefFields())) . ')';
  482. }
  483. break;
  484. }
  485. return $key;
  486. }
  487. /**
  488. * Give one xmldb_field, returns the correct "default value" for the current configuration
  489. *
  490. * @param xmldb_field $xmldb_field The field.
  491. * @return The default value of the field.
  492. */
  493. public function getDefaultValue($xmldb_field) {
  494. $default = null;
  495. if ($xmldb_field->getDefault() !== NULL) {
  496. if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
  497. $xmldb_field->getType() == XMLDB_TYPE_TEXT) {
  498. if ($xmldb_field->getDefault() === '') { // If passing empty default, use the $default_for_char one instead
  499. $default = "'" . $this->default_for_char . "'";
  500. } else {
  501. $default = "'" . $this->addslashes($xmldb_field->getDefault()) . "'";
  502. }
  503. } else {
  504. $default = $xmldb_field->getDefault();
  505. }
  506. } else {
  507. // We force default '' for not null char columns without proper default
  508. // some day this should be out!
  509. if ($this->default_for_char !== NULL &&
  510. $xmldb_field->getType() == XMLDB_TYPE_CHAR &&
  511. $xmldb_field->getNotNull()) {
  512. $default = "'" . $this->default_for_char . "'";
  513. } else {
  514. // If the DB requires to explicity define some clause to drop one default, do it here
  515. // never applying defaults to TEXT and BINARY fields
  516. if ($this->drop_default_value_required &&
  517. $xmldb_field->getType() != XMLDB_TYPE_TEXT &&
  518. $xmldb_field->getType() != XMLDB_TYPE_BINARY && !$xmldb_field->getNotNull()) {
  519. $default = $this->drop_default_value;
  520. }
  521. }
  522. }
  523. return $default;
  524. }
  525. /**
  526. * Given one xmldb_field, returns the correct "default clause" for the current configuration.
  527. *
  528. * @param xmldb_field $xmldb_field The xmldb_field.
  529. * @return The SQL clause for generating the default value as in $xmldb_field.
  530. */
  531. public function getDefaultClause($xmldb_field) {
  532. $defaultvalue = $this->getDefaultValue ($xmldb_field);
  533. if ($defaultvalue !== null) {
  534. return ' DEFAULT ' . $defaultvalue;
  535. } else {
  536. return null;
  537. }
  538. }
  539. /**
  540. * Given one correct xmldb_table and the new name, returns the SQL statements
  541. * to rename it (inside one array).
  542. *
  543. * @param xmldb_table $xmldb_table The table to rename.
  544. * @param string $newname The new name to rename the table to.
  545. * @return array SQL statement(s) to rename the table.
  546. */
  547. public function getRenameTableSQL($xmldb_table, $newname) {
  548. $results = array(); //Array where all the sentences will be stored
  549. $newt = new xmldb_table($newname); //Temporal table for name calculations
  550. $rename = str_replace('OLDNAME', $this->getTableName($xmldb_table), $this->rename_table_sql);
  551. $rename = str_replace('NEWNAME', $this->getTableName($newt), $rename);
  552. $results[] = $rename;
  553. // Call to getRenameTableExtraSQL() override if needed
  554. $extra_sentences = $this->getRenameTableExtraSQL($xmldb_table, $newname);
  555. $results = array_merge($results, $extra_sentences);
  556. return $results;
  557. }
  558. /**
  559. * Given one correct xmldb_table, returns the SQL statements
  560. * to drop it (inside one array). Works also for temporary tables.
  561. *
  562. * @param xmldb_table $xmldb_table The table to drop.
  563. * @return array SQL statement(s) for dropping the specified table.
  564. */
  565. public function getDropTableSQL($xmldb_table) {
  566. $results = array(); //Array where all the sentences will be stored
  567. $drop = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_table_sql);
  568. $results[] = $drop;
  569. // call to getDropTableExtraSQL(), override if needed
  570. $extra_sentences = $this->getDropTableExtraSQL($xmldb_table);
  571. $results = array_merge($results, $extra_sentences);
  572. return $results;
  573. }
  574. /**
  575. * Performs any clean up that needs to be done after a table is dropped.
  576. *
  577. * @param xmldb_table $table
  578. */
  579. public function cleanup_after_drop(xmldb_table $table): void {
  580. if ($this->temptables->is_temptable($table->getName())) {
  581. $this->temptables->delete_temptable($table->getName());
  582. }
  583. }
  584. /**
  585. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to add the field to the table.
  586. *
  587. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  588. * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
  589. * @param string $skip_type_clause The type clause on alter columns, NULL by default.
  590. * @param string $skip_default_clause The default clause on alter columns, NULL by default.
  591. * @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
  592. * @return array The SQL statement for adding a field to the table.
  593. */
  594. public function getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
  595. $skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
  596. $skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
  597. $skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
  598. $results = array();
  599. // Get the quoted name of the table and field
  600. $tablename = $this->getTableName($xmldb_table);
  601. // Build the standard alter table add
  602. $sql = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
  603. $skip_default_clause,
  604. $skip_notnull_clause);
  605. $altertable = 'ALTER TABLE ' . $tablename . ' ADD ' . $sql;
  606. // Add the after clause if necessary
  607. if ($this->add_after_clause && $xmldb_field->getPrevious()) {
  608. $altertable .= ' AFTER ' . $this->getEncQuoted($xmldb_field->getPrevious());
  609. }
  610. $results[] = $altertable;
  611. return $results;
  612. }
  613. /**
  614. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop the field from the table.
  615. *
  616. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  617. * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
  618. * @return array The SQL statement for dropping a field from the table.
  619. */
  620. public function getDropFieldSQL($xmldb_table, $xmldb_field) {
  621. $results = array();
  622. // Get the quoted name of the table and field
  623. $tablename = $this->getTableName($xmldb_table);
  624. $fieldname = $this->getEncQuoted($xmldb_field->getName());
  625. // Build the standard alter table drop
  626. $results[] = 'ALTER TABLE ' . $tablename . ' DROP COLUMN ' . $fieldname;
  627. return $results;
  628. }
  629. /**
  630. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to alter the field in the table.
  631. *
  632. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  633. * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
  634. * @param string $skip_type_clause The type clause on alter columns, NULL by default.
  635. * @param string $skip_default_clause The default clause on alter columns, NULL by default.
  636. * @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
  637. * @return string The field altering SQL statement.
  638. */
  639. public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
  640. $skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
  641. $skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
  642. $skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
  643. $results = array();
  644. // Get the quoted name of the table and field
  645. $tablename = $this->getTableName($xmldb_table);
  646. $fieldname = $this->getEncQuoted($xmldb_field->getName());
  647. // Build de alter sentence using the alter_column_sql template
  648. $alter = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->alter_column_sql);
  649. $colspec = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
  650. $skip_default_clause,
  651. $skip_notnull_clause,
  652. true);
  653. $alter = str_replace('COLUMNSPECS', $colspec, $alter);
  654. // Add the after clause if necessary
  655. if ($this->add_after_clause && $xmldb_field->getPrevious()) {
  656. $alter .= ' after ' . $this->getEncQuoted($xmldb_field->getPrevious());
  657. }
  658. // Build the standard alter table modify
  659. $results[] = $alter;
  660. return $results;
  661. }
  662. /**
  663. * Given one xmldb_table and one xmldb_field, return the SQL statements needed to modify the default of the field in the table.
  664. *
  665. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  666. * @param xmldb_field $xmldb_field The instance of xmldb_field to get the modified default value from.
  667. * @return array The SQL statement for modifying the default value.
  668. */
  669. public function getModifyDefaultSQL($xmldb_table, $xmldb_field) {
  670. $results = array();
  671. // Get the quoted name of the table and field
  672. $tablename = $this->getTableName($xmldb_table);
  673. $fieldname = $this->getEncQuoted($xmldb_field->getName());
  674. // Decide if we are going to create/modify or to drop the default
  675. if ($xmldb_field->getDefault() === null) {
  676. $results = $this->getDropDefaultSQL($xmldb_table, $xmldb_field); //Drop
  677. } else {
  678. $results = $this->getCreateDefaultSQL($xmldb_table, $xmldb_field); //Create/modify
  679. }
  680. return $results;
  681. }
  682. /**
  683. * Given one correct xmldb_field and the new name, returns the SQL statements
  684. * to rename it (inside one array).
  685. *
  686. * @param xmldb_table $xmldb_table The table related to $xmldb_field.
  687. * @param xmldb_field $xmldb_field The instance of xmldb_field to get the renamed field from.
  688. * @param string $newname The new name to rename the field to.
  689. * @return array The SQL statements for renaming the field.
  690. */
  691. public function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
  692. $results = array(); //Array where all the sentences will be stored
  693. // Although this is checked in database_manager::rename_field() - double check
  694. // that we aren't trying to rename one "id" field. Although it could be
  695. // implemented (if adding the necessary code to rename sequences, defaults,
  696. // triggers... and so on under each getRenameFieldExtraSQL() function, it's
  697. // better to forbid it, mainly because this field is the default PK and
  698. // in the future, a lot of FKs can be pointing here. So, this field, more
  699. // or less, must be considered immutable!
  700. if ($xmldb_field->getName() == 'id') {
  701. return array();
  702. }
  703. $rename = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_column_sql);
  704. $rename = str_replace('OLDFIELDNAME', $this->getEncQuoted($xmldb_field->getName()), $rename);
  705. $rename = str_replace('NEWFIELDNAME', $this->getEncQuoted($newname), $rename);
  706. $results[] = $rename;
  707. // Call to getRenameFieldExtraSQL(), override if needed
  708. $extra_sentences = $this->getRenameFieldExtraSQL($xmldb_table, $xmldb_field, $newname);
  709. $results = array_merge($results, $extra_sentences);
  710. return $results;
  711. }
  712. /**
  713. * Given one xmldb_table and one xmldb_key, return the SQL statements needed to add the key to the table
  714. * note that undelying indexes will be added as parametrised by $xxxx_keys and $xxxx_index parameters.
  715. *
  716. * @param xmldb_table $xmldb_table The table related to $xmldb_key.
  717. * @param xmldb_key $xmldb_key The xmldb_key to add.
  718. * @return array SQL statement to add the xmldb_key.
  719. */
  720. public function getAddKeySQL($xmldb_table, $xmldb_key) {
  721. $results = array();
  722. // Just use the CreateKeySQL function
  723. if ($keyclause = $this->getKeySQL($xmldb_table, $xmldb_key)) {
  724. $key = 'ALTER TABLE ' . $this->getTableName($xmldb_table) .
  725. ' ADD CONSTRAINT ' . $keyclause;
  726. $results[] = $key;
  727. }
  728. // If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
  729. // automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
  730. if (!$keyclause || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
  731. // Only if they don't exist
  732. if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN) { //Calculate type of index based on type ok key
  733. $indextype = XMLDB_INDEX_NOTUNIQUE;
  734. } else {
  735. $indextype = XMLDB_INDEX_UNIQUE;
  736. }
  737. $xmldb_index = new xmldb_index('anyname', $indextype, $xmldb_key->getFields());
  738. if (!$this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
  739. $results = array_merge($results, $this->getAddIndexSQL($xmldb_table, $xmldb_index));
  740. }
  741. }
  742. // If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
  743. if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
  744. //Duplicate the key
  745. $xmldb_key->setType(XMLDB_KEY_UNIQUE);
  746. $results = array_merge($results, $this->getAddKeySQL($xmldb_table, $xmldb_key));
  747. }
  748. // Return results
  749. return $results;
  750. }
  751. /**
  752. * Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
  753. *
  754. * @param xmldb_table $xmldb_table The table related to $xmldb_key.
  755. * @param xmldb_key $xmldb_key The xmldb_key to drop.
  756. * @return array SQL statement to drop the xmldb_key.
  757. */
  758. public function getDropKeySQL($xmldb_table, $xmldb_key) {
  759. $results = array();
  760. // Get the key name (note that this doesn't introspect DB, so could cause some problems sometimes!)
  761. // TODO: We'll need to overwrite the whole getDropKeySQL() method inside each DB to do the proper queries
  762. // against the dictionary or require ADOdb to support it or change the find_key_name() method to
  763. // perform DB introspection directly. But, for now, as we aren't going to enable referential integrity
  764. // it won't be a problem at all
  765. $dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
  766. // Only if such type of key generation is enabled
  767. $dropkey = false;
  768. switch ($xmldb_key->getType()) {
  769. case XMLDB_KEY_PRIMARY:
  770. if ($this->primary_keys) {
  771. $template = $this->drop_primary_key;
  772. $dropkey = true;
  773. }
  774. break;
  775. case XMLDB_KEY_UNIQUE:
  776. if ($this->unique_keys) {
  777. $template = $this->drop_unique_key;
  778. $dropkey = true;
  779. }
  780. break;
  781. case XMLDB_KEY_FOREIGN_UNIQUE:
  782. case XMLDB_KEY_FOREIGN:
  783. if ($this->foreign_keys) {
  784. $template = $this->drop_foreign_key;
  785. $dropkey = true;
  786. }
  787. break;
  788. }
  789. // If we have decided to drop the key, let's do it
  790. if ($dropkey) {
  791. // Replace TABLENAME, CONSTRAINTTYPE and KEYNAME as needed
  792. $dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $template);
  793. $dropsql = str_replace('KEYNAME', $dbkeyname, $dropsql);
  794. $results[] = $dropsql;
  795. }
  796. // If we aren't dropping the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
  797. // automatically by the RDBMS) drop the underlying (created by us) index (if exists)
  798. if (!$dropkey || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
  799. // Only if they exist
  800. $xmldb_index = new xmldb_index('anyname', XMLDB_INDEX_UNIQUE, $xmldb_key->getFields());
  801. if ($this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
  802. $results = array_merge($results, $this->getDropIndexSQL($xmldb_table, $xmldb_index));
  803. }
  804. }
  805. // If the key is XMLDB_KEY_FOREIGN_UNIQUE, drop the UNIQUE too
  806. if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
  807. //Duplicate the key
  808. $xmldb_key->setType(XMLDB_KEY_UNIQUE);
  809. $results = array_merge($results, $this->getDropKeySQL($xmldb_table, $xmldb_key));
  810. }
  811. // Return results
  812. return $results;
  813. }
  814. /**
  815. * Given one xmldb_table and one xmldb_key, return the SQL statements needed to rename the key in the table
  816. * Experimental! Shouldn't be used at all!
  817. *
  818. * @param xmldb_table $xmldb_table The table related to $xmldb_key.
  819. * @param xmldb_key $xmldb_key The xmldb_key to rename.
  820. * @param string $newname The xmldb_key's new name.
  821. * @return array SQL statement to rename the xmldb_key.
  822. */
  823. public function getRenameKeySQL($xmldb_table, $xmldb_key, $newname) {
  824. $results = array();
  825. // Get the real key name
  826. $dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
  827. // Check we are really generating this type of keys
  828. if (($xmldb_key->getType() == XMLDB_KEY_PRIMARY && !$this->primary_keys) ||
  829. ($xmldb_key->getType() == XMLDB_KEY_UNIQUE && !$this->unique_keys) ||
  830. ($xmldb_key->getType() == XMLDB_KEY_FOREIGN && !$this->foreign_keys) ||
  831. ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && !$this->unique_keys && !$this->foreign_keys)) {
  832. // We aren't generating this type of keys, delegate to child indexes
  833. $xmldb_index = new xmldb_index($xmldb_key->getName());
  834. $xmldb_index->setFields($xmldb_key->getFields());
  835. return $this->getRenameIndexSQL($xmldb_table, $xmldb_index, $newname);
  836. }
  837. // Arrived here so we are working with keys, lets rename them
  838. // Replace TABLENAME and KEYNAME as needed
  839. $renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_key_sql);
  840. $renamesql = str_replace('OLDKEYNAME', $dbkeyname, $renamesql);
  841. $renamesql = str_replace('NEWKEYNAME', $newname, $renamesql);
  842. // Some DB doesn't support key renaming so this can be empty
  843. if ($renamesql) {
  844. $results[] = $renamesql;
  845. }
  846. return $results;
  847. }
  848. /**
  849. * Given one xmldb_table and one xmldb_index, return the SQL statements needed to add the index to the table.
  850. *
  851. * @param xmldb_table $xmldb_table The xmldb_table instance to add the index on.
  852. * @param xmldb_index $xmldb_index The xmldb_index to add.
  853. * @return array An array of SQL statements to add the index.
  854. */
  855. public function getAddIndexSQL($xmldb_table, $xmldb_index) {
  856. // Just use the CreateIndexSQL function
  857. return $this->getCreateIndexSQL($xmldb_table, $xmldb_index);
  858. }
  859. /**
  860. * Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
  861. *
  862. * @param xmldb_table $xmldb_table The xmldb_table instance to drop the index on.
  863. * @param xmldb_index $xmldb_index The xmldb_index to drop.
  864. * @return array An array of SQL statements to drop the index.
  865. */
  866. public function getDropIndexSQL($xmldb_table, $xmldb_index) {
  867. $results = array();
  868. // Get the real index name
  869. $dbindexnames = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index, true);
  870. // Replace TABLENAME and INDEXNAME as needed
  871. if ($dbindexnames) {
  872. foreach ($dbindexnames as $dbindexname) {
  873. $dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
  874. $dropsql = str_replace('INDEXNAME', $this->getEncQuoted($dbindexname), $dropsql);
  875. $results[] = $dropsql;
  876. }
  877. }
  878. return $results;
  879. }
  880. /**
  881. * Given one xmldb_table and one xmldb_index, return the SQL statements needed to rename the index in the table
  882. * Experimental! Shouldn't be used at all!
  883. *
  884. * @param xmldb_table $xmldb_table The xmldb_table instance to rename the index on.
  885. * @param xmldb_index $xmldb_index The xmldb_index to rename.
  886. * @param string $newname The xmldb_index's new name.
  887. * @return array An array of SQL statements to rename the index.
  888. */
  889. function getRenameIndexSQL($xmldb_table, $xmldb_index, $newname) {
  890. // Some DB doesn't support index renaming (MySQL) so this can be empty
  891. if (empty($this->rename_index_sql)) {
  892. return array();
  893. }
  894. // Get the real index name
  895. $dbindexname = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index);
  896. // Replace TABLENAME and INDEXNAME as needed
  897. $renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_index_sql);
  898. $renamesql = str_replace('OLDINDEXNAME', $this->getEncQuoted($dbindexname), $renamesql);
  899. $renamesql = str_replace('NEWINDEXNAME', $this->getEncQuoted($newname), $renamesql);
  900. return array($renamesql);
  901. }
  902. /**
  903. * Given three strings (table name, list of fields (comma separated) and suffix),
  904. * create the proper object name quoting it if necessary.
  905. *
  906. * IMPORTANT: This function must be used to CALCULATE NAMES of objects TO BE CREATED,
  907. * NEVER TO GUESS NAMES of EXISTING objects!!!
  908. *
  909. * @param string $tablename The table name.
  910. * @param string $fields A list of comma separated fields.
  911. * @param string $suffix A suffix for the object name.
  912. * @return string Object's name.
  913. */
  914. public function getNameForObject($tablename, $fields, $suffix='') {
  915. $name = '';
  916. // Implement one basic cache to avoid object name duplication
  917. // along all the request life, but never to return cached results
  918. // We need this because sql statements are created before executing
  919. // them, hence names doesn't exist "physically" yet in DB, so we need
  920. // to known which ones have been used.
  921. // We track all the keys used, and the previous counters to make subsequent creates faster.
  922. // This may happen a lot with things like bulk backups or restores.
  923. static $usednames = array();
  924. static $previouscounters = array();
  925. // Use standard naming. See http://docs.moodle.org/en/XMLDB_key_and_index_naming
  926. $tablearr = explode ('_', $tablename);
  927. foreach ($tablearr as $table) {
  928. $name .= substr(trim($table),0,4);
  929. }
  930. $name .= '_';
  931. $fieldsarr = explode (',', $fields);
  932. foreach ($fieldsarr as $field) {
  933. $name .= substr(trim($field),0,3);
  934. }
  935. // Prepend the prefix
  936. $name = trim($this->prefix . $name);
  937. // Make sure name does not exceed the maximum name length and add suffix.
  938. $maxlengthwithoutsuffix = $this->names_max_length - strlen($suffix) - ($suffix ? 1 : 0);
  939. $namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix) . ($suffix ? ('_' . $suffix) : '');
  940. if (isset($previouscounters[$name])) {
  941. // If we have a counter stored, we will need to modify the key to the next counter location.
  942. $counter = $previouscounters[$name] + 1;
  943. $namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
  944. $counter . ($suffix ? ('_' . $suffix) : '');
  945. } else {
  946. $counter = 1;
  947. }
  948. // If the calculated name is in the cache, or if we detect it by introspecting the DB let's modify it.
  949. while (isset($usednames[$namewithsuffix]) || $this->isNameInUse($namewithsuffix, $suffix, $tablename)) {
  950. // Now iterate until not used name is found, incrementing the counter
  951. $counter++;
  952. $namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
  953. $counter . ($suffix ? ('_' . $suffix) : '');
  954. }
  955. // Add the name to the cache. Using key look with isset because it is much faster than in_array.
  956. $usednames[$namewithsuffix] = true;
  957. $previouscounters[$name] = $counter;
  958. // Quote it if necessary (reserved words)
  959. $namewithsuffix = $this->getEncQuoted($namewithsuffix);
  960. return $namewithsuffix;
  961. }
  962. /**
  963. * Given any string (or one array), enclose it by the proper quotes
  964. * if it's a reserved word
  965. *
  966. * @param string|array $input String to quote.
  967. * @return string|array Quoted string.
  968. */
  969. public function getEncQuoted($input) {
  970. if (is_array($input)) {
  971. foreach ($input as $key=>$content) {
  972. $input[$key] = $this->getEncQuoted($content);
  973. }
  974. return $input;
  975. } else {
  976. // Always lowercase
  977. $input

Large files files are truncated, but you can click here to view the full file