ImportSql.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /**
  3. * SQL import plugin for phpMyAdmin
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Plugins\Import;
  7. use PhpMyAdmin\DatabaseInterface;
  8. use PhpMyAdmin\File;
  9. use PhpMyAdmin\Plugins\ImportPlugin;
  10. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  11. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  12. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  13. use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
  14. use PhpMyAdmin\Properties\Plugins\ImportPluginProperties;
  15. use PhpMyAdmin\SqlParser\Utils\BufferedQuery;
  16. use function __;
  17. use function count;
  18. use function implode;
  19. use function mb_strlen;
  20. use function preg_replace;
  21. /**
  22. * Handles the import for the SQL format
  23. */
  24. class ImportSql extends ImportPlugin
  25. {
  26. /**
  27. * @psalm-return non-empty-lowercase-string
  28. */
  29. public function getName(): string
  30. {
  31. return 'sql';
  32. }
  33. protected function setProperties(): ImportPluginProperties
  34. {
  35. global $dbi;
  36. $importPluginProperties = new ImportPluginProperties();
  37. $importPluginProperties->setText('SQL');
  38. $importPluginProperties->setExtension('sql');
  39. $importPluginProperties->setOptionsText(__('Options'));
  40. $compats = $dbi->getCompatibilities();
  41. if (count($compats) > 0) {
  42. $values = [];
  43. foreach ($compats as $val) {
  44. $values[$val] = $val;
  45. }
  46. // create the root group that will be the options field for
  47. // $importPluginProperties
  48. // this will be shown as "Format specific options"
  49. $importSpecificOptions = new OptionsPropertyRootGroup('Format Specific Options');
  50. // general options main group
  51. $generalOptions = new OptionsPropertyMainGroup('general_opts');
  52. // create primary items and add them to the group
  53. $leaf = new SelectPropertyItem(
  54. 'compatibility',
  55. __('SQL compatibility mode:')
  56. );
  57. $leaf->setValues($values);
  58. $leaf->setDoc(
  59. [
  60. 'manual_MySQL_Database_Administration',
  61. 'Server_SQL_mode',
  62. ]
  63. );
  64. $generalOptions->addProperty($leaf);
  65. $leaf = new BoolPropertyItem(
  66. 'no_auto_value_on_zero',
  67. __('Do not use <code>AUTO_INCREMENT</code> for zero values')
  68. );
  69. $leaf->setDoc(
  70. [
  71. 'manual_MySQL_Database_Administration',
  72. 'Server_SQL_mode',
  73. 'sqlmode_no_auto_value_on_zero',
  74. ]
  75. );
  76. $generalOptions->addProperty($leaf);
  77. // add the main group to the root group
  78. $importSpecificOptions->addProperty($generalOptions);
  79. // set the options for the import plugin property item
  80. $importPluginProperties->setOptions($importSpecificOptions);
  81. }
  82. return $importPluginProperties;
  83. }
  84. /**
  85. * Handles the whole import logic
  86. *
  87. * @param array $sql_data 2-element array with sql data
  88. */
  89. public function doImport(?File $importHandle = null, array &$sql_data = []): void
  90. {
  91. global $error, $timeout_passed, $dbi;
  92. // Handle compatibility options.
  93. $this->setSQLMode($dbi, $_REQUEST);
  94. $bq = new BufferedQuery();
  95. if (isset($_POST['sql_delimiter'])) {
  96. $bq->setDelimiter($_POST['sql_delimiter']);
  97. }
  98. /**
  99. * Will be set in Import::getNextChunk().
  100. *
  101. * @global bool $GLOBALS ['finished']
  102. */
  103. $GLOBALS['finished'] = false;
  104. while (! $error && (! $timeout_passed)) {
  105. // Getting the first statement, the remaining data and the last
  106. // delimiter.
  107. $statement = $bq->extract();
  108. // If there is no full statement, we are looking for more data.
  109. if (empty($statement)) {
  110. // Importing new data.
  111. $newData = $this->import->getNextChunk($importHandle);
  112. // Subtract data we didn't handle yet and stop processing.
  113. if ($newData === false) {
  114. $GLOBALS['offset'] -= mb_strlen($bq->query);
  115. break;
  116. }
  117. // Checking if the input buffer has finished.
  118. if ($newData === true) {
  119. $GLOBALS['finished'] = true;
  120. break;
  121. }
  122. // Convert CR (but not CRLF) to LF otherwise all queries may
  123. // not get executed on some platforms.
  124. $bq->query .= preg_replace("/\r($|[^\n])/", "\n$1", $newData);
  125. continue;
  126. }
  127. // Executing the query.
  128. $this->import->runQuery($statement, $statement, $sql_data);
  129. }
  130. // Extracting remaining statements.
  131. while (! $error && ! $timeout_passed && ! empty($bq->query)) {
  132. $statement = $bq->extract(true);
  133. if (empty($statement)) {
  134. continue;
  135. }
  136. $this->import->runQuery($statement, $statement, $sql_data);
  137. }
  138. if ($GLOBALS['error']) {
  139. return;
  140. }
  141. // Finishing.
  142. $this->import->runQuery('', '', $sql_data);
  143. }
  144. /**
  145. * Handle compatibility options
  146. *
  147. * @param DatabaseInterface $dbi Database interface
  148. * @param array $request Request array
  149. */
  150. private function setSQLMode($dbi, array $request): void
  151. {
  152. $sql_modes = [];
  153. if (isset($request['sql_compatibility']) && $request['sql_compatibility'] !== 'NONE') {
  154. $sql_modes[] = $request['sql_compatibility'];
  155. }
  156. if (isset($request['sql_no_auto_value_on_zero'])) {
  157. $sql_modes[] = 'NO_AUTO_VALUE_ON_ZERO';
  158. }
  159. if (count($sql_modes) <= 0) {
  160. return;
  161. }
  162. $dbi->tryQuery(
  163. 'SET SQL_MODE="' . implode(',', $sql_modes) . '"'
  164. );
  165. }
  166. }