2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * function for the main export logic
8 declare(strict_types
=1);
12 use PhpMyAdmin\Plugins\ExportPlugin
;
13 use PhpMyAdmin\Plugins\SchemaPlugin
;
16 * PhpMyAdmin\Export class
23 * @var DatabaseInterface
29 * @param DatabaseInterface $dbi DatabaseInterface instance
31 public function __construct($dbi)
37 * Sets a session variable upon a possible fatal error during export
41 public function shutdown(): void
43 $error = error_get_last();
44 if ($error != null && mb_strpos($error['message'], "execution time")) {
45 //set session variable to check if there was error while exporting
46 $_SESSION['pma_export_error'] = $error['message'];
55 public function isGzHandlerEnabled(): bool
57 return in_array('ob_gzhandler', ob_list_handlers());
61 * Detect whether gzencode is needed; it might not be needed if
62 * the server is already compressing by itself
64 * @return bool Whether gzencode is needed
66 public function gzencodeNeeded(): bool
69 * We should gzencode only if the function exists
70 * but we don't want to compress twice, therefore
71 * gzencode only if transparent compression is not enabled
72 * and gz compression was not asked via $cfg['OBGzip']
73 * but transparent compression does not apply when saving to server
75 $chromeAndGreaterThan43 = PMA_USR_BROWSER_AGENT
== 'CHROME'
76 && PMA_USR_BROWSER_VER
>= 43; // see bug #4942
78 return function_exists('gzencode')
79 && ((! ini_get('zlib.output_compression')
80 && ! $this->isGzHandlerEnabled())
81 ||
$GLOBALS['save_on_server']
82 ||
$chromeAndGreaterThan43);
86 * Output handler for all exports, if needed buffering, it stores data into
87 * $dump_buffer, otherwise it prints them out.
89 * @param string $line the insert statement
91 * @return bool Whether output succeeded
93 public function outputHandler(?
string $line): bool
95 global $time_start, $dump_buffer, $dump_buffer_len, $save_filename;
97 // Kanji encoding convert feature
98 if ($GLOBALS['output_kanji_conversion']) {
99 $line = Encoding
::kanjiStrConv(
102 isset($GLOBALS['xkana']) ?
$GLOBALS['xkana'] : ''
106 // If we have to buffer data, we will perform everything at once at the end
107 if ($GLOBALS['buffer_needed']) {
108 $dump_buffer .= $line;
109 if ($GLOBALS['onfly_compression']) {
110 $dump_buffer_len +
= strlen($line);
112 if ($dump_buffer_len > $GLOBALS['memory_limit']) {
113 if ($GLOBALS['output_charset_conversion']) {
114 $dump_buffer = Encoding
::convertString(
120 if ($GLOBALS['compression'] == 'gzip'
121 && $this->gzencodeNeeded()
124 // without the optional parameter level because it bugs
125 $dump_buffer = gzencode($dump_buffer);
127 if ($GLOBALS['save_on_server']) {
128 $write_result = @fwrite
($GLOBALS['file_handle'], $dump_buffer);
129 // Here, use strlen rather than mb_strlen to get the length
130 // in bytes to compare against the number of bytes written.
131 if ($write_result != strlen($dump_buffer)) {
132 $GLOBALS['message'] = Message
::error(
133 __('Insufficient space to save the file %s.')
135 $GLOBALS['message']->addParam($save_filename);
142 $dump_buffer_len = 0;
146 if ($time_start >= $time_now +
30) {
147 $time_start = $time_now;
148 header('X-pmaPing: Pong');
151 } elseif ($GLOBALS['asfile']) {
152 if ($GLOBALS['output_charset_conversion']) {
153 $line = Encoding
::convertString(
159 if ($GLOBALS['save_on_server'] && mb_strlen($line) > 0) {
160 if ($GLOBALS['file_handle'] !== null) {
161 $write_result = @fwrite
($GLOBALS['file_handle'], $line);
163 $write_result = false;
165 // Here, use strlen rather than mb_strlen to get the length
166 // in bytes to compare against the number of bytes written.
168 ||
$write_result != strlen($line)
170 $GLOBALS['message'] = Message
::error(
171 __('Insufficient space to save the file %s.')
173 $GLOBALS['message']->addParam($save_filename);
177 if ($time_start >= $time_now +
30) {
178 $time_start = $time_now;
179 header('X-pmaPing: Pong');
182 // We export as file - output normally
186 // We export as html - replace special chars
187 echo htmlspecialchars($line);
193 * Returns HTML containing the footer for a displayed export
195 * @param string $back_button the link for going Back
196 * @param string $refreshButton the link for refreshing page
198 * @return string the HTML output
200 public function getHtmlForDisplayedExportFooter(
202 string $refreshButton
205 * Close the html tags and add the footers for on-screen export
210 // bottom back button
214 . '<script type="text/javascript">' . "\n"
215 . '//<![CDATA[' . "\n"
216 . 'var $body = $("body");' . "\n"
217 . '$("#textSQLDUMP")' . "\n"
218 . '.width($body.width() - 50)' . "\n"
219 . '.height($body.height() - 100);' . "\n"
221 . '</script>' . "\n";
225 * Computes the memory limit for export
227 * @return int the memory limit
229 public function getMemoryLimit(): int
231 $memory_limit = trim(ini_get('memory_limit'));
232 $memory_limit_num = (int) substr($memory_limit, 0, -1);
233 $lowerLastChar = strtolower(substr($memory_limit, -1));
235 if (empty($memory_limit) ||
'-1' == $memory_limit) {
236 $memory_limit = 2 * 1024 * 1024;
237 } elseif ($lowerLastChar == 'm') {
238 $memory_limit = $memory_limit_num * 1024 * 1024;
239 } elseif ($lowerLastChar == 'k') {
240 $memory_limit = $memory_limit_num * 1024;
241 } elseif ($lowerLastChar == 'g') {
242 $memory_limit = $memory_limit_num * 1024 * 1024 * 1024;
244 $memory_limit = (int) $memory_limit;
247 // Some of memory is needed for other things and as threshold.
248 // During export I had allocated (see memory_get_usage function)
249 // approx 1.2MB so this comes from that.
250 if ($memory_limit > 1500000) {
251 $memory_limit -= 1500000;
254 // Some memory is needed for compression, assume 1/3
256 return $memory_limit;
260 * Return the filename and MIME type for export file
262 * @param string $export_type type of export
263 * @param string $remember_template whether to remember template
264 * @param ExportPlugin $export_plugin the export plugin
265 * @param string $compression compression asked
266 * @param string $filename_template the filename template
268 * @return string[] the filename template and mime type
270 public function getFilenameAndMimetype(
272 string $remember_template,
273 ExportPlugin
$export_plugin,
275 string $filename_template
277 if ($export_type == 'server') {
278 if (! empty($remember_template)) {
279 $GLOBALS['PMA_Config']->setUserValue(
280 'pma_server_filename_template',
281 'Export/file_template_server',
285 } elseif ($export_type == 'database') {
286 if (! empty($remember_template)) {
287 $GLOBALS['PMA_Config']->setUserValue(
288 'pma_db_filename_template',
289 'Export/file_template_database',
294 if (! empty($remember_template)) {
295 $GLOBALS['PMA_Config']->setUserValue(
296 'pma_table_filename_template',
297 'Export/file_template_table',
302 $filename = Util
::expandUserString($filename_template);
303 // remove dots in filename (coming from either the template or already
304 // part of the filename) to avoid a remote code execution vulnerability
305 $filename = Sanitize
::sanitizeFilename($filename, $replaceDots = true);
307 // Grab basic dump extension and mime type
308 // Check if the user already added extension;
309 // get the substring where the extension would be if it was included
310 $extension_start_pos = mb_strlen($filename) - mb_strlen(
311 $export_plugin->getProperties()->getExtension()
313 $user_extension = mb_substr(
315 $extension_start_pos,
318 $required_extension = "." . $export_plugin->getProperties()->getExtension();
319 if (mb_strtolower($user_extension) != $required_extension) {
320 $filename .= $required_extension;
322 $mime_type = $export_plugin->getProperties()->getMimeType();
324 // If dump is going to be compressed, set correct mime_type and add
325 // compression to extension
326 if ($compression == 'gzip') {
328 $mime_type = 'application/x-gzip';
329 } elseif ($compression == 'zip') {
331 $mime_type = 'application/zip';
340 * Open the export file
342 * @param string $filename the export filename
343 * @param boolean $quick_export whether it's a quick export or not
345 * @return array the full save filename, possible message and the file handle
347 public function openFile(string $filename, bool $quick_export): array
351 $doNotSaveItOver = true;
353 if (isset($_POST['quick_export_onserver_overwrite'])) {
354 $doNotSaveItOver = $_POST['quick_export_onserver_overwrite'] != 'saveitover';
357 $save_filename = Util
::userDir($GLOBALS['cfg']['SaveDir'])
358 . preg_replace('@[/\\\\]@', '_', $filename);
360 if (@file_exists
($save_filename)
361 && ((! $quick_export && empty($_POST['onserver_overwrite']))
363 && $doNotSaveItOver))
365 $message = Message
::error(
367 'File %s already exists on server, '
368 . 'change filename or check overwrite option.'
371 $message->addParam($save_filename);
372 } elseif (@is_file
($save_filename) && ! @is_writable
($save_filename)) {
373 $message = Message
::error(
375 'The web server does not have permission '
376 . 'to save the file %s.'
379 $message->addParam($save_filename);
380 } elseif (! $file_handle = @fopen
($save_filename, 'w')) {
381 $message = Message
::error(
383 'The web server does not have permission '
384 . 'to save the file %s.'
387 $message->addParam($save_filename);
397 * Close the export file
399 * @param resource $file_handle the export file handle
400 * @param string $dump_buffer the current dump buffer
401 * @param string $save_filename the export filename
403 * @return Message a message object (or empty string)
405 public function closeFile(
408 string $save_filename
410 $write_result = @fwrite
($file_handle, $dump_buffer);
411 fclose($file_handle);
412 // Here, use strlen rather than mb_strlen to get the length
413 // in bytes to compare against the number of bytes written.
414 if (strlen($dump_buffer) > 0
415 && (! $write_result ||
$write_result != strlen($dump_buffer))
417 $message = new Message(
418 __('Insufficient space to save the file %s.'),
423 $message = new Message(
424 __('Dump has been saved to file %s.'),
433 * Compress the export buffer
435 * @param array|string $dump_buffer the current dump buffer
436 * @param string $compression the compression mode
437 * @param string $filename the filename
439 * @return array|string|bool
441 public function compress($dump_buffer, string $compression, string $filename)
443 if ($compression == 'zip' && function_exists('gzcompress')) {
444 $zipExtension = new ZipExtension();
445 $filename = substr($filename, 0, -4); // remove extension (.zip)
446 $dump_buffer = $zipExtension->createFile($dump_buffer, $filename);
447 } elseif ($compression == 'gzip' && $this->gzencodeNeeded()) {
448 // without the optional parameter level because it bugs
449 $dump_buffer = gzencode($dump_buffer);
455 * Saves the dump_buffer for a particular table in an array
456 * Used in separate files export
458 * @param string $object_name the name of current object to be stored
459 * @param boolean $append optional boolean to append to an existing index or not
463 public function saveObjectInBuffer(string $object_name, bool $append = false): void
465 global $dump_buffer_objects, $dump_buffer, $dump_buffer_len;
467 if (! empty($dump_buffer)) {
468 if ($append && isset($dump_buffer_objects[$object_name])) {
469 $dump_buffer_objects[$object_name] .= $dump_buffer;
471 $dump_buffer_objects[$object_name] = $dump_buffer;
477 $dump_buffer_len = 0;
481 * Returns HTML containing the header for a displayed export
483 * @param string $export_type the export type
484 * @param string $db the database name
485 * @param string $table the table name
487 * @return string[] the generated HTML and back button
489 public function getHtmlForDisplayedExportHeader(
497 * Displays a back button with all the $_POST data in the URL
498 * (store in a variable to also display after the textarea)
500 $back_button = '<p id="export_back_button">[ <a href="';
501 if ($export_type == 'server') {
502 $back_button .= 'server_export.php" data-post="' . Url
::getCommon([], '');
503 } elseif ($export_type == 'database') {
504 $back_button .= 'db_export.php" data-post="' . Url
::getCommon(['db' => $db], '');
506 $back_button .= 'tbl_export.php" data-post="' . Url
::getCommon(
515 // Convert the multiple select elements from an array to a string
516 if ($export_type == 'database') {
517 $structOrDataForced = empty($_POST['structure_or_data_forced']);
518 if ($structOrDataForced && ! isset($_POST['table_structure'])) {
519 $_POST['table_structure'] = [];
521 if ($structOrDataForced && ! isset($_POST['table_data'])) {
522 $_POST['table_data'] = [];
526 foreach ($_POST as $name => $value) {
527 if (! is_array($value)) {
528 $back_button .= '&' . urlencode((string) $name) . '=' . urlencode((string) $value);
531 $back_button .= '&repopulate=1">' . __('Back') . '</a> ]</p>';
533 $html .= $back_button;
534 $refreshButton = '<form id="export_refresh_form" method="POST" action="export.php" class="disableAjax">';
535 $refreshButton .= '[ <a class="disableAjax" onclick="$(this).parent().submit()">' . __('Refresh') . '</a> ]';
536 foreach ($_POST as $name => $value) {
537 if (is_array($value)) {
538 foreach ($value as $val) {
539 $refreshButton .= '<input type="hidden" name="' . htmlentities((string) $name) . '[]" value="' . htmlentities((string) $val) . '">';
542 $refreshButton .= '<input type="hidden" name="' . htmlentities((string) $name) . '" value="' . htmlentities((string) $value) . '">';
545 $refreshButton .= '</form>';
546 $html .= $refreshButton
548 . '<form name="nofunction">'
549 . '<textarea name="sqldump" cols="50" rows="30" '
550 . 'id="textSQLDUMP" wrap="OFF">';
560 * Export at the server level
562 * @param string|array $db_select the selected databases to export
563 * @param string $whatStrucOrData structure or data or both
564 * @param ExportPlugin $export_plugin the selected export plugin
565 * @param string $crlf end of line character(s)
566 * @param string $err_url the URL in case of error
567 * @param string $export_type the export type
568 * @param bool $do_relation whether to export relation info
569 * @param bool $do_comments whether to add comments
570 * @param bool $do_mime whether to add MIME info
571 * @param bool $do_dates whether to add dates
572 * @param array $aliases alias information for db/table/column
573 * @param string $separate_files whether it is a separate-files export
577 public function exportServer(
579 string $whatStrucOrData,
580 ExportPlugin
$export_plugin,
589 string $separate_files
591 if (! empty($db_select)) {
592 $tmp_select = implode('|', $db_select);
593 $tmp_select = '|' . $tmp_select . '|';
595 // Walk over databases
596 foreach ($GLOBALS['dblist']->databases
as $current_db) {
597 if (isset($tmp_select)
598 && mb_strpos(' ' . $tmp_select, '|' . $current_db . '|')
600 $tables = $this->dbi
->getTables($current_db);
601 $this->exportDatabase(
616 $separate_files == 'database' ?
$separate_files : ''
618 if ($separate_files == 'server') {
619 $this->saveObjectInBuffer($current_db);
622 } // end foreach database
626 * Export at the database level
628 * @param string $db the database to export
629 * @param array $tables the tables to export
630 * @param string $whatStrucOrData structure or data or both
631 * @param array $table_structure whether to export structure for each table
632 * @param array $table_data whether to export data for each table
633 * @param ExportPlugin $export_plugin the selected export plugin
634 * @param string $crlf end of line character(s)
635 * @param string $err_url the URL in case of error
636 * @param string $export_type the export type
637 * @param bool $do_relation whether to export relation info
638 * @param bool $do_comments whether to add comments
639 * @param bool $do_mime whether to add MIME info
640 * @param bool $do_dates whether to add dates
641 * @param array $aliases Alias information for db/table/column
642 * @param string $separate_files whether it is a separate-files export
646 public function exportDatabase(
649 string $whatStrucOrData,
650 array $table_structure,
652 ExportPlugin
$export_plugin,
661 string $separate_files
663 $db_alias = ! empty($aliases[$db]['alias'])
664 ?
$aliases[$db]['alias'] : '';
666 if (! $export_plugin->exportDBHeader($db, $db_alias)) {
669 if (! $export_plugin->exportDBCreate($db, $export_type, $db_alias)) {
672 if ($separate_files == 'database') {
673 $this->saveObjectInBuffer('database', true);
676 if (($GLOBALS['sql_structure_or_data'] == 'structure'
677 ||
$GLOBALS['sql_structure_or_data'] == 'structure_and_data')
678 && isset($GLOBALS['sql_procedure_function'])
680 $export_plugin->exportRoutines($db, $aliases);
682 if ($separate_files == 'database') {
683 $this->saveObjectInBuffer('routines');
689 foreach ($tables as $table) {
690 $_table = new Table($table, $db);
691 // if this is a view, collect it for later;
692 // views must be exported after the tables
693 $is_view = $_table->isView();
697 if (($whatStrucOrData == 'structure'
698 ||
$whatStrucOrData == 'structure_and_data')
699 && in_array($table, $table_structure)
701 // for a view, export a stand-in definition of the table
702 // to resolve view dependencies (only when it's a single-file export)
704 if ($separate_files == ''
705 && isset($GLOBALS['sql_create_view'])
706 && ! $export_plugin->exportStructure(
722 } elseif (isset($GLOBALS['sql_create_table'])) {
723 $table_size = $GLOBALS['maxsize'];
724 // Checking if the maximum table size constrain has been set
725 // And if that constrain is a valid number or not
726 if ($table_size !== '' && is_numeric($table_size)) {
727 // This obtains the current table's size
728 $query = 'SELECT data_length + index_length
729 from information_schema.TABLES
730 WHERE table_schema = "' . $this->dbi
->escapeString($db) . '"
731 AND table_name = "' . $this->dbi
->escapeString($table) . '"';
733 $size = $this->dbi
->fetchValue($query);
734 //Converting the size to MB
735 $size = ($size / 1024) / 1024;
736 if ($size > $table_size) {
741 if (! $export_plugin->exportStructure(
758 // if this is a view or a merge table, don't export data
759 if (($whatStrucOrData == 'data' ||
$whatStrucOrData == 'structure_and_data')
760 && in_array($table, $table_data)
763 $tableObj = new Table($table, $db);
764 $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true);
766 $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols)
767 . ' FROM ' . Util
::backquote($db)
768 . '.' . Util
::backquote($table);
770 if (! $export_plugin->exportData(
782 // this buffer was filled, we save it and go to the next one
783 if ($separate_files == 'database') {
784 $this->saveObjectInBuffer('table_' . $table);
787 // now export the triggers (needs to be done after the data because
788 // triggers can modify already imported tables)
789 if (isset($GLOBALS['sql_create_trigger']) && ($whatStrucOrData == 'structure'
790 ||
$whatStrucOrData == 'structure_and_data')
791 && in_array($table, $table_structure)
793 if (! $export_plugin->exportStructure(
809 if ($separate_files == 'database') {
810 $this->saveObjectInBuffer('table_' . $table, true);
815 if (isset($GLOBALS['sql_create_view'])) {
816 foreach ($views as $view) {
817 // no data export for a view
818 if ($whatStrucOrData == 'structure'
819 ||
$whatStrucOrData == 'structure_and_data'
821 if (! $export_plugin->exportStructure(
837 if ($separate_files == 'database') {
838 $this->saveObjectInBuffer('view_' . $view);
844 if (! $export_plugin->exportDBFooter($db)) {
848 // export metadata related to this db
849 if (isset($GLOBALS['sql_metadata'])) {
850 // Types of metadata to export.
851 // In the future these can be allowed to be selected by the user
852 $metadataTypes = $this->getMetadataTypes();
853 $export_plugin->exportMetadata($db, $tables, $metadataTypes);
855 if ($separate_files == 'database') {
856 $this->saveObjectInBuffer('metadata');
860 if ($separate_files == 'database') {
861 $this->saveObjectInBuffer('extra');
864 if (($GLOBALS['sql_structure_or_data'] == 'structure'
865 ||
$GLOBALS['sql_structure_or_data'] == 'structure_and_data')
866 && isset($GLOBALS['sql_procedure_function'])
868 $export_plugin->exportEvents($db);
870 if ($separate_files == 'database') {
871 $this->saveObjectInBuffer('events');
877 * Export at the table level
879 * @param string $db the database to export
880 * @param string $table the table to export
881 * @param string $whatStrucOrData structure or data or both
882 * @param ExportPlugin $export_plugin the selected export plugin
883 * @param string $crlf end of line character(s)
884 * @param string $err_url the URL in case of error
885 * @param string $export_type the export type
886 * @param bool $do_relation whether to export relation info
887 * @param bool $do_comments whether to add comments
888 * @param bool $do_mime whether to add MIME info
889 * @param bool $do_dates whether to add dates
890 * @param string|null $allrows whether "dump all rows" was ticked
891 * @param string $limit_to upper limit
892 * @param string $limit_from starting limit
893 * @param string $sql_query query for which exporting is requested
894 * @param array $aliases Alias information for db/table/column
898 public function exportTable(
901 string $whatStrucOrData,
902 ExportPlugin
$export_plugin,
916 $db_alias = ! empty($aliases[$db]['alias'])
917 ?
$aliases[$db]['alias'] : '';
918 if (! $export_plugin->exportDBHeader($db, $db_alias)) {
926 $add_query = ' LIMIT '
927 . ($limit_from > 0 ?
$limit_from . ', ' : '')
933 $_table = new Table($table, $db);
934 $is_view = $_table->isView();
935 if ($whatStrucOrData == 'structure'
936 ||
$whatStrucOrData == 'structure_and_data'
939 if (isset($GLOBALS['sql_create_view'])) {
940 if (! $export_plugin->exportStructure(
956 } elseif (isset($GLOBALS['sql_create_table'])) {
957 if (! $export_plugin->exportStructure(
974 // If this is an export of a single view, we have to export data;
975 // for example, a PDF report
976 // if it is a merge table, no data is exported
977 if ($whatStrucOrData == 'data'
978 ||
$whatStrucOrData == 'structure_and_data'
980 if (! empty($sql_query)) {
981 // only preg_replace if needed
982 if (! empty($add_query)) {
983 // remove trailing semicolon before adding a LIMIT
984 $sql_query = preg_replace('%;\s*$%', '', $sql_query);
986 $local_query = $sql_query . $add_query;
987 $this->dbi
->selectDb($db);
989 // Data is exported only for Non-generated columns
990 $tableObj = new Table($table, $db);
991 $nonGeneratedCols = $tableObj->getNonGeneratedColumns(true);
993 $local_query = 'SELECT ' . implode(', ', $nonGeneratedCols)
994 . ' FROM ' . Util
::backquote($db)
995 . '.' . Util
::backquote($table) . $add_query;
997 if (! $export_plugin->exportData(
1008 // now export the triggers (needs to be done after the data because
1009 // triggers can modify already imported tables)
1010 if (isset($GLOBALS['sql_create_trigger']) && ($whatStrucOrData == 'structure'
1011 ||
$whatStrucOrData == 'structure_and_data')
1013 if (! $export_plugin->exportStructure(
1029 if (! $export_plugin->exportDBFooter($db)) {
1033 if (isset($GLOBALS['sql_metadata'])) {
1034 // Types of metadata to export.
1035 // In the future these can be allowed to be selected by the user
1036 $metadataTypes = $this->getMetadataTypes();
1037 $export_plugin->exportMetadata($db, $table, $metadataTypes);
1042 * Loads correct page after doing export
1044 * @param string $db the database name
1045 * @param string $table the table name
1046 * @param string $export_type Export type
1050 public function showPage(string $db, string $table, string $export_type): void
1053 if ($export_type == 'server') {
1054 $active_page = 'server_export.php';
1055 include_once ROOT_PATH
. 'server_export.php';
1056 } elseif ($export_type == 'database') {
1057 $active_page = 'db_export.php';
1058 include_once ROOT_PATH
. 'db_export.php';
1060 $active_page = 'tbl_export.php';
1061 include_once ROOT_PATH
. 'tbl_export.php';
1067 * Merge two alias arrays, if array1 and array2 have
1068 * conflicting alias then array2 value is used if it
1069 * is non empty otherwise array1 value.
1071 * @param array $aliases1 first array of aliases
1072 * @param array $aliases2 second array of aliases
1074 * @return array resultant merged aliases info
1076 public function mergeAliases(array $aliases1, array $aliases2): array
1078 // First do a recursive array merge
1079 // on aliases arrays.
1080 $aliases = array_merge_recursive($aliases1, $aliases2);
1081 // Now, resolve conflicts in aliases, if any
1082 foreach ($aliases as $db_name => $db) {
1083 // If alias key is an array then
1084 // it is a merge conflict.
1085 if (isset($db['alias']) && is_array($db['alias'])) {
1086 $val1 = $db['alias'][0];
1087 $val2 = $db['alias'][1];
1088 // Use aliases2 alias if non empty
1089 $aliases[$db_name]['alias']
1090 = empty($val2) ?
$val1 : $val2;
1092 if (! isset($db['tables'])) {
1095 foreach ($db['tables'] as $tbl_name => $tbl) {
1096 if (isset($tbl['alias']) && is_array($tbl['alias'])) {
1097 $val1 = $tbl['alias'][0];
1098 $val2 = $tbl['alias'][1];
1099 // Use aliases2 alias if non empty
1100 $aliases[$db_name]['tables'][$tbl_name]['alias']
1101 = empty($val2) ?
$val1 : $val2;
1103 if (! isset($tbl['columns'])) {
1106 foreach ($tbl['columns'] as $col => $col_as) {
1107 if (isset($col_as) && is_array($col_as)) {
1110 // Use aliases2 alias if non empty
1111 $aliases[$db_name]['tables'][$tbl_name]['columns'][$col]
1112 = empty($val2) ?
$val1 : $val2;
1123 * @param string $db database name
1124 * @param array $tables list of table names
1125 * @param string $lockType lock type; "[LOW_PRIORITY] WRITE" or "READ [LOCAL]"
1127 * @return mixed result of the query
1129 public function lockTables(string $db, array $tables, string $lockType = "WRITE")
1132 foreach ($tables as $table) {
1133 $locks[] = Util
::backquote($db) . "."
1134 . Util
::backquote($table) . " " . $lockType;
1137 $sql = "LOCK TABLES " . implode(", ", $locks);
1138 return $this->dbi
->tryQuery($sql);
1142 * Releases table locks
1144 * @return mixed result of the query
1146 public function unlockTables()
1148 return $this->dbi
->tryQuery("UNLOCK TABLES");
1152 * Returns all the metadata types that can be exported with a database or a table
1154 * @return string[] metadata types.
1156 public function getMetadataTypes(): array
1173 * Returns the checked clause, depending on the presence of key in array
1175 * @param string $key the key to look for
1176 * @param array $array array to verify
1178 * @return string the checked clause
1180 public function getCheckedClause(string $key, array $array): string
1182 if (in_array($key, $array)) {
1183 return ' checked="checked"';
1190 * get all the export options and verify
1191 * call and include the appropriate Schema Class depending on $export_type
1193 * @param string|null $export_type format of the export
1197 public function processExportSchema(?
string $export_type): void
1200 * default is PDF, otherwise validate it's only letters a-z
1202 if (! isset($export_type) ||
! preg_match('/^[a-zA-Z]+$/', $export_type)) {
1203 $export_type = 'pdf';
1206 // sanitize this parameter which will be used below in a file inclusion
1207 $export_type = Core
::securePath($export_type);
1209 // get the specific plugin
1210 /** @var SchemaPlugin $export_plugin */
1211 $export_plugin = Plugins
::getPlugin(
1214 'libraries/classes/Plugins/Schema/'
1217 // Check schema export type
1218 if ($export_plugin === null ||
! is_object($export_plugin)) {
1219 Core
::fatalError(__('Bad type!'));
1222 $this->dbi
->selectDb($GLOBALS['db']);
1223 $export_plugin->exportSchema($GLOBALS['db']);