/libraries/src/Schema/ChangeItem/SqlsrvChangeItem.php

https://github.com/joomla/joomla-cms · PHP · 145 lines · 65 code · 18 blank · 62 comment · 9 complexity · b4f2f97d22f88fb70311cb511fb25111 MD5 · raw file

  1. <?php
  2. /**
  3. * Joomla! Content Management System
  4. *
  5. * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
  6. * @license GNU General Public License version 2 or later; see LICENSE.txt
  7. */
  8. namespace Joomla\CMS\Schema\ChangeItem;
  9. use Joomla\CMS\Schema\ChangeItem;
  10. /**
  11. * Checks the database schema against one SQL Server DDL query to see if it has been run.
  12. *
  13. * @since 2.5
  14. */
  15. class SqlsrvChangeItem extends ChangeItem
  16. {
  17. /**
  18. * Checks a DDL query to see if it is a known type
  19. * If yes, build a check query to see if the DDL has been run on the database.
  20. * If successful, the $msgElements, $queryType, $checkStatus and $checkQuery fields are populated.
  21. * The $msgElements contains the text to create the user message.
  22. * The $checkQuery contains the SQL query to check whether the schema change has
  23. * been run against the current database. The $queryType contains the type of
  24. * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX).
  25. * The $checkStatus field is set to zero if the query is created
  26. *
  27. * If not successful, $checkQuery is empty and , and $checkStatus is -1.
  28. * For example, this will happen if the current line is a non-DDL statement.
  29. *
  30. * @return void
  31. *
  32. * @since 2.5
  33. */
  34. protected function buildCheckQuery()
  35. {
  36. // Initialize fields in case we can't create a check query
  37. // Change status to skipped
  38. $this->checkStatus = -1;
  39. $result = null;
  40. // Remove any newlines
  41. $this->updateQuery = str_replace("\n", '', $this->updateQuery);
  42. // Fix up extra spaces around () and in general
  43. $find = array('#((\s*)\(\s*([^)\s]+)\s*)(\))#', '#(\s)(\s*)#');
  44. $replace = array('($3)', '$1');
  45. $updateQuery = preg_replace($find, $replace, $this->updateQuery);
  46. $wordArray = explode(' ', $updateQuery);
  47. // First, make sure we have an array of at least 6 elements
  48. // if not, we can't make a check query for this one
  49. if (\count($wordArray) < 6) {
  50. // Done with method
  51. return;
  52. }
  53. // We can only make check queries for alter table and create table queries
  54. $command = strtoupper($wordArray[0] . ' ' . $wordArray[1]);
  55. if ($command === 'ALTER TABLE') {
  56. $alterCommand = strtoupper($wordArray[3] . ' ' . $wordArray[4]);
  57. if ($alterCommand === 'ADD') {
  58. $result = 'SELECT * FROM INFORMATION_SCHEMA.Columns ' . $wordArray[2] . ' WHERE COLUMN_NAME = ' . $this->fixQuote($wordArray[5]);
  59. $this->queryType = 'ADD';
  60. $this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]));
  61. } elseif ($alterCommand === 'CREATE INDEX') {
  62. $index = $this->fixQuote(substr($wordArray[5], 0, strpos($wordArray[5], '(')));
  63. $result = 'SELECT * FROM SYS.INDEXES ' . $wordArray[2] . ' WHERE name = ' . $index;
  64. $this->queryType = 'CREATE INDEX';
  65. $this->msgElements = array($this->fixQuote($wordArray[2]), $index);
  66. } elseif (strtoupper($wordArray[3]) === 'MODIFY' || strtoupper($wordArray[3]) === 'CHANGE') {
  67. $result = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = ' . $this->fixQuote($wordArray[2]);
  68. $this->queryType = 'ALTER COLUMN COLUMN_NAME =' . $this->fixQuote($wordArray[4]);
  69. $this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[4]));
  70. }
  71. }
  72. if ($command === 'CREATE TABLE') {
  73. $table = $wordArray[2];
  74. $result = 'SELECT * FROM sys.TABLES WHERE NAME = ' . $this->fixQuote($table);
  75. $this->queryType = 'CREATE_TABLE';
  76. $this->msgElements = array($this->fixQuote($table));
  77. }
  78. // Set fields based on results
  79. if ($this->checkQuery = $result) {
  80. // Unchecked status
  81. $this->checkStatus = 0;
  82. } else {
  83. // Skipped
  84. $this->checkStatus = -1;
  85. }
  86. }
  87. /**
  88. * Fix up integer. Fixes problem with MySQL integer descriptions.
  89. * If you change a column to "integer unsigned" it shows
  90. * as "int(10) unsigned" in the check query.
  91. *
  92. * @param string $type1 the column type
  93. * @param string $type2 the column attributes
  94. *
  95. * @return string The original or changed column type.
  96. *
  97. * @since 2.5
  98. */
  99. private function fixInteger($type1, $type2)
  100. {
  101. $result = $type1;
  102. if (strtolower($type1) === 'integer' && strtolower(substr($type2, 0, 8)) === 'unsigned') {
  103. $result = 'int';
  104. }
  105. return $result;
  106. }
  107. /**
  108. * Fixes up a string for inclusion in a query.
  109. * Replaces name quote character with normal quote for literal.
  110. * Drops trailing semicolon. Injects the database prefix.
  111. *
  112. * @param string $string The input string to be cleaned up.
  113. *
  114. * @return string The modified string.
  115. *
  116. * @since 2.5
  117. */
  118. private function fixQuote($string)
  119. {
  120. $string = str_replace('[', '', $string);
  121. $string = str_replace(']', '', $string);
  122. $string = str_replace('"', '', $string);
  123. $string = str_replace(';', '', $string);
  124. $string = str_replace('#__', $this->db->getPrefix(), $string);
  125. return $this->db->quote($string);
  126. }
  127. }