ExportSql   F
last analyzed

Complexity

Total Complexity 332

Size/Duplication

Total Lines 2765
Duplicated Lines 0 %

Test Coverage

Coverage 80.6%

Importance

Changes 0
Metric Value
wmc 332
eloc 1453
c 0
b 0
f 0
dl 0
loc 2765
rs 0.8
ccs 1259
cts 1562
cp 0.806

40 Methods

Rating   Name   Duplication   Size   Complexity  
A exportRawQuery() 0 7 2
C getTableComments() 0 118 14
B exportDBCreate() 0 47 9
B exportMetadata() 0 38 6
F getTableDefForView() 0 61 12
A exportUseStatement() 0 15 2
A exportFooter() 0 30 5
A getTableDefStandIn() 0 31 5
A exportDBFooter() 0 29 5
B exportRoutines() 0 46 6
D exportHeader() 0 90 12
A exportDBHeader() 0 18 2
A exportComment() 0 22 5
B exportEvents() 0 50 8
A possibleCRLF() 0 7 2
D exportConfigurationMetadata() 0 149 15
A makeCreateTableMSSQLCompatible() 0 81 1
A setType() 0 7 2
F setProperties() 0 388 13
A setInsertSyntax() 0 7 2
A hasCreateTable() 0 3 1
B getTableStatus() 0 39 9
A setCompatibility() 0 7 2
A useSqlBackquotes() 0 3 1
A hasCreateTrigger() 0 3 1
A hasCreateView() 0 3 1
A generateComment() 0 33 4
A hasCreateProcedureFunction() 0 3 1
F exportData() 0 274 56
F getTableDef() 0 359 55
A hasMetadata() 0 3 1
D replaceWithAliases() 0 170 35
A getName() 0 3 1
A setAutoIncrement() 0 3 1
A setMaxQuerySize() 0 11 5
C exportStructure() 0 126 14
B exportRoutineSQL() 0 60 8
B setExportOptions() 0 76 1
A addCompatOptions() 0 18 2
A setStringValue() 0 11 5

How to fix   Complexity   

Complex Class

Complex classes like ExportSql often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ExportSql, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Set of functions used to build SQL dumps of tables
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin\Plugins\Export;
9
10
use DateTimeImmutable;
11
use PhpMyAdmin\Charsets;
12
use PhpMyAdmin\Config;
13
use PhpMyAdmin\Current;
14
use PhpMyAdmin\Database\Events;
15
use PhpMyAdmin\Database\Routines;
16
use PhpMyAdmin\Database\RoutineType;
17
use PhpMyAdmin\Dbal\ConnectionType;
18
use PhpMyAdmin\Dbal\DatabaseInterface;
19
use PhpMyAdmin\Exceptions\ExportException;
20
use PhpMyAdmin\Export\Export;
21
use PhpMyAdmin\Export\StructureOrData;
22
use PhpMyAdmin\Http\ServerRequest;
23
use PhpMyAdmin\Plugins\ExportPlugin;
24
use PhpMyAdmin\Plugins\ExportType;
25
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
26
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
27
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
28
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
29
use PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem;
30
use PhpMyAdmin\Properties\Options\Items\NumberPropertyItem;
31
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
32
use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
33
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
34
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
35
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
36
use PhpMyAdmin\SqlParser\Context;
37
use PhpMyAdmin\SqlParser\Parser;
38
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
39
use PhpMyAdmin\SqlParser\Token;
40
use PhpMyAdmin\SqlParser\TokenType;
41
use PhpMyAdmin\Triggers\Triggers;
42
use PhpMyAdmin\UniqueCondition;
43
use PhpMyAdmin\Util;
44
use PhpMyAdmin\Version;
45
46
use function __;
47
use function array_keys;
48
use function bin2hex;
49
use function explode;
50
use function implode;
51
use function in_array;
52
use function is_array;
53
use function is_numeric;
54
use function is_string;
55
use function mb_strlen;
56
use function mb_strpos;
57
use function mb_substr;
58
use function preg_quote;
59
use function preg_replace;
60
use function preg_split;
61
use function sprintf;
62
use function str_contains;
63
use function str_repeat;
64
use function str_replace;
65
use function strtoupper;
66
67
use const PHP_VERSION;
68
69
/**
70
 * Handles the export for the SQL class
71
 */
72
class ExportSql extends ExportPlugin
73
{
74
    /**
75
     * Whether charset header was sent.
76
     */
77
    private bool $sentCharset = false;
78
79
    private bool $doComments = false;
80
    private bool $doDates = false;
81
    private bool $doMime = false;
82
    private bool $doRelation = false;
83
    private string $headerComment = '';
84
    public string|null $sqlAutoIncrements = null;
85
    public string|null $sqlConstraints = null;
86
    public string $sqlConstraintsQuery = '';
87
    public string|null $sqlIndexes = null;
88
    private string $sqlViews = '';
89
    private bool $useSqlBackquotes = false;
90
    private bool $useTransaction = false;
91
    private bool $disableForeignKey = false;
92
93
    /** @var 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'NONE'|'ANSI'|'DB2'|'MAX...|'ORACLE'|'TRADITIONAL' at position 0 could not be parsed: Unknown type name ''NONE'' at position 0 in 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL'.
Loading history...
94
    private string $compatibility = 'NONE';
95
96
    private bool $createDatabase = false;
97
    private bool $dropTable = false;
98
    private bool $procedureFunction = false;
99
    private bool $createTable = false;
100
101
    /** @var 'INSERT'|'UPDATE'|'REPLACE' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'INSERT'|'UPDATE'|'REPLACE' at position 0 could not be parsed: Unknown type name ''INSERT'' at position 0 in 'INSERT'|'UPDATE'|'REPLACE'.
Loading history...
102
    private string $type = 'INSERT';
103
104
    private bool $createView = false;
105
    private bool $createTrigger = false;
106
    private bool $viewCurrentUser = false;
107
    private bool $simpleViewExport = false;
108
    private bool $ifNotExists = false;
109
    private bool $orReplaceView = false;
110
    private bool $autoIncrement = false;
111
    private bool $truncate = false;
112
    private bool $delayed = false;
113
    private bool $ignore = false;
114
115
    /** @var 'complete'|'extended'|'both'|'none' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'complete'|'extended'|'both'|'none' at position 0 could not be parsed: Unknown type name ''complete'' at position 0 in 'complete'|'extended'|'both'|'none'.
Loading history...
116
    private string $insertSyntax = 'both';
117
118
    /** @var int<0, max> */
119
    private int $maxQuerySize = 50000;
120
121
    private bool $hexForBinary = false;
122
    private bool $utcTime = false;
123
    private bool $dropDatabase = false;
124
    private bool $viewsAsTables = false;
125
    private bool $metadata = false;
126
127
    public static string $oldTimezone = '';
128
    public static bool $noConstraintsComments = false;
129
    private static bool $exportingMetadata = false;
130
131
    /** @psalm-return non-empty-lowercase-string */
132
    public function getName(): string
133
    {
134
        return 'sql';
135
    }
136
137 100
    public function useSqlBackquotes(bool $useSqlBackquotes): void
138
    {
139 100
        $this->useSqlBackquotes = $useSqlBackquotes;
140
    }
141
142 100
    protected function setProperties(): ExportPluginProperties
143
    {
144 100
        $hideSql = false;
145 100
        $hideStructure = false;
146 100
        if (ExportPlugin::$exportType === ExportType::Table && ! ExportPlugin::$singleTable) {
147 100
            $hideStructure = true;
148 100
            $hideSql = true;
149
        }
150
151
        // In case we have `raw_query` parameter set,
152
        // we initialize SQL option
153 100
        if (isset($_REQUEST['raw_query'])) {
154
            $hideStructure = false;
155
            $hideSql = false;
156
        }
157
158 100
        $exportPluginProperties = new ExportPluginProperties();
159 100
        $exportPluginProperties->setText('SQL');
160 100
        $exportPluginProperties->setExtension('sql');
161 100
        $exportPluginProperties->setMimeType('text/x-sql');
162
163 100
        if ($hideSql) {
164 100
            return $exportPluginProperties;
165
        }
166
167 4
        $exportPluginProperties->setOptionsText(__('Options'));
168
169
        // create the root group that will be the options field for
170
        // $exportPluginProperties
171
        // this will be shown as "Format specific options"
172 4
        $exportSpecificOptions = new OptionsPropertyRootGroup('Format Specific Options');
173
174
        // general options main group
175 4
        $generalOptions = new OptionsPropertyMainGroup('general_opts');
176
177
        // comments
178 4
        $subgroup = new OptionsPropertySubgroup('include_comments');
179 4
        $leaf = new BoolPropertyItem(
180 4
            'include_comments',
181 4
            __(
182 4
                'Display comments <i>(includes info such as export timestamp, PHP version, and server version)</i>',
183 4
            ),
184 4
        );
185 4
        $subgroup->setSubgroupHeader($leaf);
186
187 4
        $leaf = new TextPropertyItem(
188 4
            'header_comment',
189 4
            __('Additional custom header comment (\n splits lines):'),
190 4
        );
191 4
        $subgroup->addProperty($leaf);
192 4
        $leaf = new BoolPropertyItem(
193 4
            'dates',
194 4
            __(
195 4
                'Include a timestamp of when databases were created, last updated, and last checked',
196 4
            ),
197 4
        );
198 4
        $subgroup->addProperty($leaf);
199 4
        $relationParameters = $this->relation->getRelationParameters();
200 4
        if ($relationParameters->relationFeature !== null) {
201 4
            $leaf = new BoolPropertyItem(
202 4
                'relation',
203 4
                __('Display foreign key relationships'),
204 4
            );
205 4
            $subgroup->addProperty($leaf);
206
        }
207
208 4
        if ($relationParameters->browserTransformationFeature !== null) {
209 4
            $leaf = new BoolPropertyItem(
210 4
                'mime',
211 4
                __('Display media types'),
212 4
            );
213 4
            $subgroup->addProperty($leaf);
214
        }
215
216 4
        $generalOptions->addProperty($subgroup);
217
218
        // enclose in a transaction
219 4
        $leaf = new BoolPropertyItem(
220 4
            'use_transaction',
221 4
            __('Enclose export in a transaction'),
222 4
        );
223 4
        $leaf->setDoc(
224 4
            ['programs', 'mysqldump', 'option_mysqldump_single-transaction'],
225 4
        );
226 4
        $generalOptions->addProperty($leaf);
227
228
        // disable foreign key checks
229 4
        $leaf = new BoolPropertyItem(
230 4
            'disable_fk',
231 4
            __('Disable foreign key checks'),
232 4
        );
233 4
        $leaf->setDoc(
234 4
            ['manual_MySQL_Database_Administration', 'server-system-variables', 'sysvar_foreign_key_checks'],
235 4
        );
236 4
        $generalOptions->addProperty($leaf);
237
238
        // export views as tables
239 4
        $leaf = new BoolPropertyItem(
240 4
            'views_as_tables',
241 4
            __('Export views as tables'),
242 4
        );
243 4
        $generalOptions->addProperty($leaf);
244
245
        // export metadata
246 4
        $leaf = new BoolPropertyItem(
247 4
            'metadata',
248 4
            __('Export metadata'),
249 4
        );
250 4
        $generalOptions->addProperty($leaf);
251
252 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

252
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
253
        // compatibility maximization
254 4
        $compats = $dbi->getCompatibilities();
255 4
        if ($compats !== []) {
256 4
            $this->addCompatOptions($compats, $generalOptions);
257
        }
258
259
        // what to dump (structure/data/both)
260 4
        $subgroup = new OptionsPropertySubgroup(
261 4
            'dump_table',
262 4
            __('Dump table'),
263 4
        );
264 4
        $leaf = new RadioPropertyItem('structure_or_data');
265 4
        $leaf->setValues(
266 4
            ['structure' => __('structure'), 'data' => __('data'), 'structure_and_data' => __('structure and data')],
267 4
        );
268 4
        $subgroup->setSubgroupHeader($leaf);
269 4
        $generalOptions->addProperty($subgroup);
270
271
        // add the main group to the root group
272 4
        $exportSpecificOptions->addProperty($generalOptions);
273
274
        // structure options main group
275 4
        if (! $hideStructure) {
276 4
            $structureOptions = new OptionsPropertyMainGroup(
277 4
                'structure',
278 4
                __('Object creation options'),
279 4
            );
280 4
            $structureOptions->setForce('data');
281
282
            // begin SQL Statements
283 4
            $subgroup = new OptionsPropertySubgroup();
284 4
            $leaf = new MessageOnlyPropertyItem(
285 4
                'add_statements',
286 4
                __('Add statements:'),
287 4
            );
288 4
            $subgroup->setSubgroupHeader($leaf);
289
290
            // server export options
291 4
            if (ExportPlugin::$exportType === ExportType::Server) {
292 4
                $leaf = new BoolPropertyItem(
293 4
                    'drop_database',
294 4
                    sprintf(__('Add %s statement'), '<code>DROP DATABASE IF EXISTS</code>'),
295 4
                );
296 4
                $subgroup->addProperty($leaf);
297
            }
298
299 4
            if (ExportPlugin::$exportType === ExportType::Database) {
300
                $createClause = '<code>CREATE DATABASE / USE</code>';
301
                $leaf = new BoolPropertyItem(
302
                    'create_database',
303
                    sprintf(__('Add %s statement'), $createClause),
304
                );
305
                $subgroup->addProperty($leaf);
306
            }
307
308 4
            if (ExportPlugin::$exportType === ExportType::Table) {
309
                $dropClause = $dbi->getTable(Current::$database, Current::$table)->isView()
310
                    ? '<code>DROP VIEW</code>'
311
                    : '<code>DROP TABLE</code>';
312
            } else {
313 4
                $dropClause = '<code>DROP TABLE / VIEW / PROCEDURE / FUNCTION / EVENT</code>';
314
            }
315
316 4
            $dropClause .= '<code> / TRIGGER</code>';
317
318 4
            $leaf = new BoolPropertyItem(
319 4
                'drop_table',
320 4
                sprintf(__('Add %s statement'), $dropClause),
321 4
            );
322 4
            $subgroup->addProperty($leaf);
323
324 4
            $subgroupCreateTable = new OptionsPropertySubgroup();
325
326
            // Add table structure option
327 4
            $leaf = new BoolPropertyItem(
328 4
                'create_table',
329 4
                sprintf(__('Add %s statement'), '<code>CREATE TABLE</code>'),
330 4
            );
331 4
            $subgroupCreateTable->setSubgroupHeader($leaf);
332
333 4
            $leaf = new BoolPropertyItem(
334 4
                'if_not_exists',
335 4
                '<code>IF NOT EXISTS</code> ' . __(
336 4
                    '(less efficient as indexes will be generated during table creation)',
337 4
                ),
338 4
            );
339 4
            $subgroupCreateTable->addProperty($leaf);
340
341 4
            $leaf = new BoolPropertyItem(
342 4
                'auto_increment',
343 4
                sprintf(__('%s value'), '<code>AUTO_INCREMENT</code>'),
344 4
            );
345 4
            $subgroupCreateTable->addProperty($leaf);
346
347 4
            $subgroup->addProperty($subgroupCreateTable);
348
349
            // Add view option
350 4
            $subgroupCreateView = new OptionsPropertySubgroup();
351 4
            $leaf = new BoolPropertyItem(
352 4
                'create_view',
353 4
                sprintf(__('Add %s statement'), '<code>CREATE VIEW</code>'),
354 4
            );
355 4
            $subgroupCreateView->setSubgroupHeader($leaf);
356
357 4
            $leaf = new BoolPropertyItem(
358 4
                'simple_view_export',
359
                /* l10n: Allow simplifying exported view syntax to only "CREATE VIEW" */
360 4
                __('Use simple view export'),
361 4
            );
362 4
            $subgroupCreateView->addProperty($leaf);
363
364 4
            $leaf = new BoolPropertyItem(
365 4
                'view_current_user',
366 4
                __('Exclude definition of current user'),
367 4
            );
368 4
            $subgroupCreateView->addProperty($leaf);
369
370 4
            $leaf = new BoolPropertyItem(
371 4
                'or_replace_view',
372 4
                sprintf(__('%s view'), '<code>OR REPLACE</code>'),
373 4
            );
374 4
            $subgroupCreateView->addProperty($leaf);
375
376 4
            $subgroup->addProperty($subgroupCreateView);
377
378 4
            $leaf = new BoolPropertyItem(
379 4
                'procedure_function',
380 4
                sprintf(
381 4
                    __('Add %s statement'),
382 4
                    '<code>CREATE PROCEDURE / FUNCTION / EVENT</code>',
383 4
                ),
384 4
            );
385 4
            $subgroup->addProperty($leaf);
386
387
            // Add triggers option
388 4
            $leaf = new BoolPropertyItem(
389 4
                'create_trigger',
390 4
                sprintf(__('Add %s statement'), '<code>CREATE TRIGGER</code>'),
391 4
            );
392 4
            $subgroup->addProperty($leaf);
393
394 4
            $structureOptions->addProperty($subgroup);
395
396 4
            $leaf = new BoolPropertyItem(
397 4
                'backquotes',
398 4
                __(
399 4
                    'Enclose table and column names with backquotes '
400 4
                    . '<i>(Protects column and table names formed with'
401 4
                    . ' special characters or keywords)</i>',
402 4
                ),
403 4
            );
404
405 4
            $structureOptions->addProperty($leaf);
406
407
            // add the main group to the root group
408 4
            $exportSpecificOptions->addProperty($structureOptions);
409
        }
410
411
        // begin Data options
412 4
        $dataOptions = new OptionsPropertyMainGroup(
413 4
            'data',
414 4
            __('Data creation options'),
415 4
        );
416 4
        $dataOptions->setForce('structure');
417 4
        $leaf = new BoolPropertyItem(
418 4
            'truncate',
419 4
            __('Truncate table before insert'),
420 4
        );
421 4
        $dataOptions->addProperty($leaf);
422
423
        // begin SQL Statements
424 4
        $subgroup = new OptionsPropertySubgroup();
425 4
        $leaf = new MessageOnlyPropertyItem(
426 4
            __('Instead of <code>INSERT</code> statements, use:'),
427 4
        );
428 4
        $subgroup->setSubgroupHeader($leaf);
429
430 4
        $leaf = new BoolPropertyItem(
431 4
            'delayed',
432 4
            __('<code>INSERT DELAYED</code> statements'),
433 4
        );
434 4
        $leaf->setDoc(
435 4
            ['manual_MySQL_Database_Administration', 'insert_delayed'],
436 4
        );
437 4
        $subgroup->addProperty($leaf);
438
439 4
        $leaf = new BoolPropertyItem(
440 4
            'ignore',
441 4
            __('<code>INSERT IGNORE</code> statements'),
442 4
        );
443 4
        $leaf->setDoc(
444 4
            ['manual_MySQL_Database_Administration', 'insert'],
445 4
        );
446 4
        $subgroup->addProperty($leaf);
447 4
        $dataOptions->addProperty($subgroup);
448
449
        // Function to use when dumping dat
450 4
        $leaf = new SelectPropertyItem(
451 4
            'type',
452 4
            __('Function to use when dumping data:'),
453 4
        );
454 4
        $leaf->setValues(
455 4
            ['INSERT' => 'INSERT', 'UPDATE' => 'UPDATE', 'REPLACE' => 'REPLACE'],
456 4
        );
457 4
        $dataOptions->addProperty($leaf);
458
459
        /* Syntax to use when inserting data */
460 4
        $subgroup = new OptionsPropertySubgroup();
461 4
        $leaf = new MessageOnlyPropertyItem(
462 4
            null,
463 4
            __('Syntax to use when inserting data:'),
464 4
        );
465 4
        $subgroup->setSubgroupHeader($leaf);
466 4
        $leaf = new RadioPropertyItem(
467 4
            'insert_syntax',
468 4
            __('<code>INSERT IGNORE</code> statements'),
469 4
        );
470 4
        $leaf->setValues(
471 4
            [
472 4
                'complete' => __(
473 4
                    'include column names in every <code>INSERT</code> statement'
474 4
                    . ' <br> &nbsp; &nbsp; &nbsp; Example: <code>INSERT INTO'
475 4
                    . ' tbl_name (col_A,col_B,col_C) VALUES (1,2,3)</code>',
476 4
                ),
477 4
                'extended' => __(
478 4
                    'insert multiple rows in every <code>INSERT</code> statement'
479 4
                    . '<br> &nbsp; &nbsp; &nbsp; Example: <code>INSERT INTO'
480 4
                    . ' tbl_name VALUES (1,2,3), (4,5,6), (7,8,9)</code>',
481 4
                ),
482 4
                'both' => __(
483 4
                    'both of the above<br> &nbsp; &nbsp; &nbsp; Example:'
484 4
                    . ' <code>INSERT INTO tbl_name (col_A,col_B,col_C) VALUES'
485 4
                    . ' (1,2,3), (4,5,6), (7,8,9)</code>',
486 4
                ),
487 4
                'none' => __(
488 4
                    'neither of the above<br> &nbsp; &nbsp; &nbsp; Example:'
489 4
                    . ' <code>INSERT INTO tbl_name VALUES (1,2,3)</code>',
490 4
                ),
491 4
            ],
492 4
        );
493 4
        $subgroup->addProperty($leaf);
494 4
        $dataOptions->addProperty($subgroup);
495
496
        // Max length of query
497 4
        $leaf = new NumberPropertyItem(
498 4
            'max_query_size',
499 4
            __('Maximal length of created query'),
500 4
        );
501 4
        $dataOptions->addProperty($leaf);
502
503
        // Dump binary columns in hexadecimal
504 4
        $leaf = new BoolPropertyItem(
505 4
            'hex_for_binary',
506 4
            __(
507 4
                'Dump binary columns in hexadecimal notation <i>(for example, "abc" becomes 0x616263)</i>',
508 4
            ),
509 4
        );
510 4
        $dataOptions->addProperty($leaf);
511
512
        // Dump time in UTC
513 4
        $leaf = new BoolPropertyItem(
514 4
            'utc_time',
515 4
            __(
516 4
                'Dump TIMESTAMP columns in UTC <i>(enables TIMESTAMP columns'
517 4
                . ' to be dumped and reloaded between servers in different'
518 4
                . ' time zones)</i>',
519 4
            ),
520 4
        );
521 4
        $dataOptions->addProperty($leaf);
522
523
        // add the main group to the root group
524 4
        $exportSpecificOptions->addProperty($dataOptions);
525
526
        // set the options for the export plugin property item
527 4
        $exportPluginProperties->setOptions($exportSpecificOptions);
528
529 4
        return $exportPluginProperties;
530
    }
531
532
    /**
533
     * Generates SQL for routines export
534
     *
535
     * @param string   $db        Database
536
     * @param mixed[]  $aliases   Aliases of db/table/columns
537
     * @param string   $name      Verbose name of exported routine
538
     * @param string[] $routines  List of routines to export
539
     * @param string   $delimiter Delimiter to use in SQL
540
     * @psalm-param 'FUNCTION'|'PROCEDURE' $type
541
     *
542
     * @return string SQL query
543
     */
544 4
    protected function exportRoutineSQL(
545
        string $db,
546
        array $aliases,
547
        string $type,
548
        string $name,
549
        array $routines,
550
        string $delimiter,
551
    ): string {
552 4
        $text = $this->exportComment()
553 4
            . $this->exportComment($name)
554 4
            . $this->exportComment();
555
556 4
        $usedAlias = false;
557 4
        $procQuery = '';
558
559 4
        foreach ($routines as $routine) {
560 4
            if ($this->dropTable) {
561 4
                $procQuery .= 'DROP ' . $type . ' IF EXISTS '
562 4
                    . Util::backquote($routine)
563 4
                    . $delimiter . "\n";
564
            }
565
566 4
            $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

566
            $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
567 4
            if ($type === 'FUNCTION') {
568 4
                $definition = Routines::getFunctionDefinition($dbi, $db, $routine);
569
            } else {
570 4
                $definition = Routines::getProcedureDefinition($dbi, $db, $routine);
571
            }
572
573 4
            $flag = false;
574 4
            $createQuery = $this->replaceWithAliases($delimiter, $definition, $aliases, $db, $flag);
575 4
            if ($createQuery !== '' && Config::getInstance()->settings['Export']['remove_definer_from_definitions']) {
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

575
            if ($createQuery !== '' && /** @scrutinizer ignore-deprecated */ Config::getInstance()->settings['Export']['remove_definer_from_definitions']) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
576
                // Remove definer clause from routine definitions
577
                $parser = new Parser('DELIMITER ' . $delimiter . "\n" . $createQuery);
578
                $statement = $parser->statements[0];
579
                $statement->options->remove('DEFINER');
580
                $createQuery = $statement->build();
581
            }
582
583
            // One warning per database
584 4
            if ($flag) {
585
                $usedAlias = true;
586
            }
587
588 4
            $procQuery .= $createQuery . $delimiter . "\n\n";
589
        }
590
591 4
        if ($usedAlias) {
592
            $text .= $this->exportComment(
593
                __('It appears your database uses routines;'),
594
            )
595
            . $this->exportComment(
596
                __('alias export may not work reliably in all cases.'),
597
            )
598
            . $this->exportComment();
599
        }
600
601 4
        $text .= $procQuery;
602
603 4
        return $text;
604
    }
605
606
    /**
607
     * Exports routines (procedures and functions)
608
     *
609
     * @param string  $db      Database
610
     * @param mixed[] $aliases Aliases of db/table/columns
611
     */
612 4
    public function exportRoutines(string $db, array $aliases = []): bool
613
    {
614 4
        $dbAlias = $db;
615 4
        $this->initAlias($aliases, $dbAlias);
616
617 4
        $text = '';
618 4
        $delimiter = '$$';
619
620 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

620
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
621 4
        $procedureNames = Routines::getNames($dbi, $db, RoutineType::Procedure);
622 4
        $functionNames = Routines::getNames($dbi, $db, RoutineType::Function);
623
624 4
        if ($procedureNames || $functionNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $functionNames of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $procedureNames of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
625 4
            $text .= "\n"
626 4
                . 'DELIMITER ' . $delimiter . "\n";
627
628 4
            if ($procedureNames !== []) {
629 4
                $text .= $this->exportRoutineSQL(
630 4
                    $db,
631 4
                    $aliases,
632 4
                    'PROCEDURE',
633 4
                    __('Procedures'),
634 4
                    $procedureNames,
635 4
                    $delimiter,
636 4
                );
637
            }
638
639 4
            if ($functionNames !== []) {
640 4
                $text .= $this->exportRoutineSQL(
641 4
                    $db,
642 4
                    $aliases,
643 4
                    'FUNCTION',
644 4
                    __('Functions'),
645 4
                    $functionNames,
646 4
                    $delimiter,
647 4
                );
648
            }
649
650 4
            $text .= 'DELIMITER ;' . "\n";
651
        }
652
653 4
        if ($text !== '') {
654 4
            return $this->export->outputHandler($text);
655
        }
656
657
        return false;
658
    }
659
660
    /**
661
     * Possibly outputs comment
662
     *
663
     * @param string $text Text of comment
664
     *
665
     * @return string The formatted comment
666
     */
667 44
    private function exportComment(string $text = ''): string
668
    {
669 44
        if ($this->doComments) {
670
            // see https://dev.mysql.com/doc/refman/5.0/en/ansi-diff-comments.html
671 28
            if ($text === '') {
672 28
                return '--' . "\n";
673
            }
674
675 28
            $lines = preg_split("/\\r\\n|\\r|\\n/", $text);
676 28
            if ($lines === false) {
677
                return '--' . "\n";
678
            }
679
680 28
            $result = [];
681 28
            foreach ($lines as $line) {
682 28
                $result[] = '-- ' . $line . "\n";
683
            }
684
685 28
            return implode('', $result);
686
        }
687
688 20
        return '';
689
    }
690
691
    /**
692
     * Possibly outputs CRLF
693
     *
694
     * @return string crlf or nothing
695
     */
696 28
    private function possibleCRLF(): string
697
    {
698 28
        if ($this->doComments) {
699 20
            return "\n";
700
        }
701
702 12
        return '';
703
    }
704
705
    /**
706
     * Outputs export footer
707
     */
708 4
    public function exportFooter(): bool
709
    {
710 4
        $foot = '';
711
712 4
        if ($this->disableForeignKey) {
713 4
            $foot .= 'SET FOREIGN_KEY_CHECKS=1;' . "\n";
714
        }
715
716 4
        if ($this->useTransaction) {
717 4
            $foot .= 'COMMIT;' . "\n";
718
        }
719
720
        // restore connection settings
721 4
        if ($this->sentCharset) {
722
            $foot .= "\n"
723
                . '/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;'
724
                . "\n"
725
                . '/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;'
726
                . "\n"
727
                . '/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;'
728
                . "\n";
729
            $this->sentCharset = false;
730
        }
731
732
        /* Restore timezone */
733 4
        if ($this->utcTime) {
734 4
            DatabaseInterface::getInstance()->query('SET time_zone = "' . self::$oldTimezone . '"');
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

734
            /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->query('SET time_zone = "' . self::$oldTimezone . '"');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
735
        }
736
737 4
        return $this->export->outputHandler($foot);
738
    }
739
740
    /**
741
     * Outputs export header. It is the first method to be called, so all
742
     * the required variables are initialized here.
743
     */
744 4
    public function exportHeader(): bool
745
    {
746 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

746
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
747
748 4
        $dbi->tryQuery('SET SQL_MODE="' . ($this->compatibility !== 'NONE' ? $this->compatibility : '') . '"');
749
750 4
        $head = $this->exportComment('phpMyAdmin SQL Dump')
751 4
            . $this->exportComment('version ' . Version::VERSION)
752 4
            . $this->exportComment('https://www.phpmyadmin.net/')
753 4
            . $this->exportComment();
754 4
        $config = Config::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

754
        $config = /** @scrutinizer ignore-deprecated */ Config::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
755 4
        $hostString = __('Host:') . ' ' . $config->selectedServer['host'];
756 4
        if (! empty($config->selectedServer['port'])) {
757 4
            $hostString .= ':' . $config->selectedServer['port'];
758
        }
759
760 4
        $head .= $this->exportComment($hostString);
761 4
        $head .= $this->exportComment(
762 4
            __('Generation Time:') . ' '
763 4
            . Util::localisedDate(new DateTimeImmutable()),
764 4
        )
765 4
        . $this->exportComment(
766 4
            __('Server version:') . ' ' . $dbi->getVersionString(),
767 4
        )
768 4
        . $this->exportComment(__('PHP Version:') . ' ' . PHP_VERSION)
769 4
        . $this->possibleCRLF();
770
771 4
        if ($this->headerComment !== '') {
772
            // '\n' is not a newline (like "\n" would be), it's the characters
773
            // backslash and n, as explained on the export interface
774 4
            $lines = explode('\n', $this->headerComment);
775 4
            $head .= $this->exportComment();
776 4
            foreach ($lines as $oneLine) {
777 4
                $head .= $this->exportComment($oneLine);
778
            }
779
780 4
            $head .= $this->exportComment();
781
        }
782
783 4
        if ($this->disableForeignKey) {
784 4
            $head .= 'SET FOREIGN_KEY_CHECKS=0;' . "\n";
785
        }
786
787
        // We want exported AUTO_INCREMENT columns to have still same value,
788
        // do this only for recent MySQL exports
789 4
        if ($this->compatibility === 'NONE') {
790 4
            $head .= 'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";' . "\n";
791
        }
792
793 4
        if ($this->useTransaction) {
794 4
            $head .= 'START TRANSACTION;' . "\n";
795
        }
796
797
        /* Change timezone if we should export timestamps in UTC */
798 4
        if ($this->utcTime) {
799 4
            $head .= 'SET time_zone = "+00:00";' . "\n";
800 4
            self::$oldTimezone = $dbi->fetchValue('SELECT @@session.time_zone');
801 4
            $dbi->query('SET time_zone = "+00:00"');
802
        }
803
804 4
        $head .= $this->possibleCRLF();
805
806 4
        if (Export::$asFile) {
807
            // we are saving as file, therefore we provide charset information
808
            // so that a utility like the mysql client can interpret
809
            // the file correctly
810 4
            if (isset(Current::$charset, Charsets::$mysqlCharsetMap[Current::$charset])) {
811
                // we got a charset from the export dialog
812 4
                $setNames = Charsets::$mysqlCharsetMap[Current::$charset];
813
            } else {
814
                // by default we use the connection charset
815
                $setNames = Charsets::$mysqlCharsetMap['utf-8'];
816
            }
817
818 4
            if ($setNames === 'utf8') {
819 4
                $setNames = $dbi->getDefaultCharset();
820
            }
821
822 4
            $head .= "\n"
823 4
                . '/*!40101 SET @OLD_CHARACTER_SET_CLIENT='
824 4
                . '@@CHARACTER_SET_CLIENT */;' . "\n"
825 4
                . '/*!40101 SET @OLD_CHARACTER_SET_RESULTS='
826 4
                . '@@CHARACTER_SET_RESULTS */;' . "\n"
827 4
                . '/*!40101 SET @OLD_COLLATION_CONNECTION='
828 4
                . '@@COLLATION_CONNECTION */;' . "\n"
829 4
                . '/*!40101 SET NAMES ' . $setNames . ' */;' . "\n\n";
830 4
            $this->sentCharset = true;
831
        }
832
833 4
        return $this->export->outputHandler($head);
834
    }
835
836
    /**
837
     * Outputs CREATE DATABASE statement
838
     *
839
     * @param string $db      Database name
840
     * @param string $dbAlias Aliases of db
841
     */
842 4
    public function exportDBCreate(string $db, string $dbAlias = ''): bool
843
    {
844 4
        if ($dbAlias === '') {
845 4
            $dbAlias = $db;
846
        }
847
848 4
        if ($this->structureOrData !== StructureOrData::Data && $this->dropDatabase) {
849
            if (
850 4
                ! $this->export->outputHandler(
851 4
                    'DROP DATABASE IF EXISTS '
852 4
                    . Util::backquoteCompat(
853 4
                        $dbAlias,
854 4
                        $this->compatibility,
855 4
                        $this->useSqlBackquotes,
856 4
                    )
857 4
                    . ';' . "\n",
858 4
                )
859
            ) {
860
                return false;
861
            }
862
        }
863
864 4
        if (ExportPlugin::$exportType === ExportType::Database && ! $this->createDatabase) {
865
            return true;
866
        }
867
868 4
        $createQuery = 'CREATE DATABASE IF NOT EXISTS '
869 4
            . Util::backquoteCompat($dbAlias, $this->compatibility, $this->useSqlBackquotes);
870 4
        $collation = DatabaseInterface::getInstance()->getDbCollation($db);
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

870
        $collation = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->getDbCollation($db);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
871 4
        if (str_contains($collation, '_')) {
872 4
            $createQuery .= ' DEFAULT CHARACTER SET '
873 4
                . mb_substr(
874 4
                    $collation,
875 4
                    0,
876 4
                    (int) mb_strpos($collation, '_'),
877 4
                )
878 4
                . ' COLLATE ' . $collation;
879
        } else {
880 4
            $createQuery .= ' DEFAULT CHARACTER SET ' . $collation;
881
        }
882
883 4
        $createQuery .= ';' . "\n";
884 4
        if (! $this->export->outputHandler($createQuery)) {
885
            return false;
886
        }
887
888 4
        return $this->exportUseStatement($dbAlias, $this->compatibility);
889
    }
890
891
    /**
892
     * Outputs USE statement
893
     *
894
     * @param string $db     db to use
895
     * @param string $compat sql compatibility
896
     */
897 4
    private function exportUseStatement(string $db, string $compat): bool
898
    {
899 4
        if ($compat === 'NONE') {
900 4
            return $this->export->outputHandler(
901 4
                'USE '
902 4
                . Util::backquoteCompat(
903 4
                    $db,
904 4
                    $compat,
905 4
                    $this->useSqlBackquotes,
906 4
                )
907 4
                . ';' . "\n",
908 4
            );
909
        }
910
911
        return $this->export->outputHandler('USE ' . $db . ';' . "\n");
912
    }
913
914
    /**
915
     * Outputs database header
916
     *
917
     * @param string $db      Database name
918
     * @param string $dbAlias Alias of db
919
     */
920 4
    public function exportDBHeader(string $db, string $dbAlias = ''): bool
921
    {
922 4
        if ($dbAlias === '') {
923 4
            $dbAlias = $db;
924
        }
925
926 4
        $head = $this->exportComment()
927 4
            . $this->exportComment(
928 4
                __('Database:') . ' '
929 4
                . Util::backquoteCompat(
930 4
                    $dbAlias,
931 4
                    $this->compatibility,
932 4
                    $this->useSqlBackquotes,
933 4
                ),
934 4
            )
935 4
            . $this->exportComment();
936
937 4
        return $this->export->outputHandler($head);
938
    }
939
940
    /**
941
     * Outputs database footer
942
     *
943
     * @param string $db Database name
944
     */
945 4
    public function exportDBFooter(string $db): bool
946
    {
947 4
        $result = true;
948
949
        //add indexes to the sql dump file
950 4
        if ($this->sqlIndexes !== null) {
951
            $result = $this->export->outputHandler($this->sqlIndexes);
952
            $this->sqlIndexes = null;
953
        }
954
955
        //add auto increments to the sql dump file
956 4
        if ($this->sqlAutoIncrements !== null) {
957
            $result = $this->export->outputHandler($this->sqlAutoIncrements);
958
            $this->sqlAutoIncrements = null;
959
        }
960
961
        //add views to the sql dump file
962 4
        if ($this->sqlViews !== '') {
963
            $result = $this->export->outputHandler($this->sqlViews);
964
            $this->sqlViews = '';
965
        }
966
967
        //add constraints to the sql dump file
968 4
        if ($this->sqlConstraints !== null) {
969 4
            $result = $this->export->outputHandler($this->sqlConstraints);
970 4
            $this->sqlConstraints = null;
971
        }
972
973 4
        return $result;
974
    }
975
976
    /**
977
     * Exports events
978
     *
979
     * @param string $db Database
980
     */
981 4
    public function exportEvents(string $db): bool
982
    {
983 4
        $text = '';
984 4
        $delimiter = '$$';
985
986 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

986
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
987 4
        $eventNames = $dbi->fetchSingleColumn(
988 4
            'SELECT EVENT_NAME FROM information_schema.EVENTS WHERE'
989 4
            . ' EVENT_SCHEMA= ' . $dbi->quoteString($db),
990 4
        );
991
992 4
        if ($eventNames !== []) {
993 4
            $text .= "\n"
994 4
                . 'DELIMITER ' . $delimiter . "\n";
995
996 4
            $text .= $this->exportComment()
997 4
                . $this->exportComment(__('Events'))
998 4
                . $this->exportComment();
999
1000 4
            foreach ($eventNames as $eventName) {
1001 4
                if ($this->dropTable) {
1002
                    $text .= 'DROP EVENT IF EXISTS '
1003
                        . Util::backquote($eventName)
1004
                        . $delimiter . "\n";
1005
                }
1006
1007 4
                $eventDef = Events::getDefinition($dbi, $db, $eventName);
1008
                if (
1009 4
                    $eventDef !== null
1010 4
                    && $eventDef !== ''
1011 4
                    && Config::getInstance()->settings['Export']['remove_definer_from_definitions']
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1011
                    && /** @scrutinizer ignore-deprecated */ Config::getInstance()->settings['Export']['remove_definer_from_definitions']

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1012
                ) {
1013
                    // remove definer clause from the event definition
1014
                    $parser = new Parser('DELIMITER ' . $delimiter . "\n" . $eventDef);
1015
                    $statement = $parser->statements[0];
1016
                    $statement->options->remove('DEFINER');
1017
                    $eventDef = $statement->build();
1018
                }
1019
1020 4
                $text .= $eventDef . $delimiter . "\n\n";
1021
            }
1022
1023 4
            $text .= 'DELIMITER ;' . "\n";
1024
        }
1025
1026 4
        if ($text !== '') {
1027 4
            return $this->export->outputHandler($text);
1028
        }
1029
1030
        return false;
1031
    }
1032
1033
    /**
1034
     * Exports metadata from Configuration Storage
1035
     *
1036
     * @param string          $db            database being exported
1037
     * @param string|string[] $tables        table(s) being exported
1038
     * @param string[]        $metadataTypes types of metadata to export
1039
     */
1040
    public function exportMetadata(
1041
        string $db,
1042
        string|array $tables,
1043
        array $metadataTypes,
1044
    ): bool {
1045
        $relationParameters = $this->relation->getRelationParameters();
1046
        if ($relationParameters->db === null) {
1047
            return true;
1048
        }
1049
1050
        $comment = $this->possibleCRLF()
1051
            . $this->possibleCRLF()
1052
            . $this->exportComment()
1053
            . $this->exportComment(__('Metadata'))
1054
            . $this->exportComment();
1055
        if (! $this->export->outputHandler($comment)) {
1056
            return false;
1057
        }
1058
1059
        if (! $this->exportUseStatement((string) $relationParameters->db, $this->compatibility)) {
1060
            return false;
1061
        }
1062
1063
        $r = 1;
1064
        if (is_array($tables)) {
0 ignored issues
show
introduced by
The condition is_array($tables) is always true.
Loading history...
1065
            // export metadata for each table
1066
            foreach ($tables as $table) {
1067
                $r &= (int) $this->exportConfigurationMetadata($db, $table, $metadataTypes);
1068
            }
1069
1070
            // export metadata for the database
1071
            $r &= (int) $this->exportConfigurationMetadata($db, null, $metadataTypes);
1072
        } else {
1073
            // export metadata for single table
1074
            $r &= (int) $this->exportConfigurationMetadata($db, $tables, $metadataTypes);
1075
        }
1076
1077
        return (bool) $r;
1078
    }
1079
1080
    /**
1081
     * Exports metadata from Configuration Storage
1082
     *
1083
     * @param string      $db            database being exported
1084
     * @param string|null $table         table being exported
1085
     * @param string[]    $metadataTypes types of metadata to export
1086
     */
1087
    private function exportConfigurationMetadata(
1088
        string $db,
1089
        string|null $table,
1090
        array $metadataTypes,
1091
    ): bool {
1092
        $relationParameters = $this->relation->getRelationParameters();
1093
        $relationParams = $relationParameters->toArray();
1094
1095
        if (isset($table)) {
1096
            $types = ['column_info' => 'db_name', 'table_uiprefs' => 'db_name', 'tracking' => 'db_name'];
1097
        } else {
1098
            $types = [
1099
                'bookmark' => 'dbase',
1100
                'relation' => 'master_db',
1101
                'pdf_pages' => 'db_name',
1102
                'savedsearches' => 'db_name',
1103
                'central_columns' => 'db_name',
1104
            ];
1105
        }
1106
1107
        $aliases = [];
1108
1109
        $comment = $this->possibleCRLF() . $this->exportComment();
1110
1111
        if ($table !== null) {
1112
            $comment .= $this->exportComment(
1113
                sprintf(
1114
                    __('Metadata for table %s'),
1115
                    $table,
1116
                ),
1117
            );
1118
        } else {
1119
            $comment .= $this->exportComment(
1120
                sprintf(
1121
                    __('Metadata for database %s'),
1122
                    $db,
1123
                ),
1124
            );
1125
        }
1126
1127
        $comment .= $this->exportComment();
1128
1129
        if (! $this->export->outputHandler($comment)) {
1130
            return false;
1131
        }
1132
1133
        foreach ($types as $type => $dbNameColumn) {
1134
            if (! in_array($type, $metadataTypes) || ! isset($relationParams[$type])) {
1135
                continue;
1136
            }
1137
1138
            $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1138
            $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1139
            // special case, designer pages and their coordinates
1140
            if ($type === 'pdf_pages') {
1141
                if ($relationParameters->pdfFeature === null) {
1142
                    continue;
1143
                }
1144
1145
                $sqlQuery = 'SELECT `page_nr`, `page_descr` FROM '
1146
                    . Util::backquote($relationParameters->pdfFeature->database)
1147
                    . '.' . Util::backquote($relationParameters->pdfFeature->pdfPages)
1148
                    . ' WHERE `db_name` = ' . $dbi->quoteString($db);
1149
1150
                $result = $dbi->fetchResult($sqlQuery, 'page_nr', 'page_descr');
1151
1152
                foreach (array_keys($result) as $page) {
1153
                    // insert row for pdf_page
1154
                    $sqlQueryRow = 'SELECT `db_name`, `page_descr` FROM '
1155
                        . Util::backquote($relationParameters->pdfFeature->database)
1156
                        . '.' . Util::backquote($relationParameters->pdfFeature->pdfPages)
1157
                        . ' WHERE `db_name` = ' . $dbi->quoteString($db)
1158
                        . ' AND `page_nr` = ' . (int) $page;
1159
1160
                    if (
1161
                        ! $this->exportData(
1162
                            $relationParameters->pdfFeature->database->getName(),
1163
                            $relationParameters->pdfFeature->pdfPages->getName(),
1164
                            $sqlQueryRow,
1165
                            $aliases,
1166
                        )
1167
                    ) {
1168
                        return false;
1169
                    }
1170
1171
                    $lastPage = "\n"
1172
                        . 'SET @LAST_PAGE = LAST_INSERT_ID();'
1173
                        . "\n";
1174
                    if (! $this->export->outputHandler($lastPage)) {
1175
                        return false;
1176
                    }
1177
1178
                    $sqlQueryCoords = 'SELECT `db_name`, `table_name`, '
1179
                        . "'@LAST_PAGE' AS `pdf_page_number`, `x`, `y` FROM "
1180
                        . Util::backquote($relationParameters->pdfFeature->database)
1181
                        . '.' . Util::backquote($relationParameters->pdfFeature->tableCoords)
1182
                        . " WHERE `pdf_page_number` = '" . $page . "'";
1183
1184
                    self::$exportingMetadata = true;
1185
                    if (
1186
                        ! $this->exportData(
1187
                            $relationParameters->pdfFeature->database->getName(),
1188
                            $relationParameters->pdfFeature->tableCoords->getName(),
1189
                            $sqlQueryCoords,
1190
                            $aliases,
1191
                        )
1192
                    ) {
1193
                        self::$exportingMetadata = false;
1194
1195
                        return false;
1196
                    }
1197
1198
                    self::$exportingMetadata = false;
1199
                }
1200
1201
                continue;
1202
            }
1203
1204
            // remove auto_incrementing id field for some tables
1205
            $sqlQuery = match ($type) {
1206
                'bookmark' => 'SELECT `dbase`, `user`, `label`, `query` FROM ',
1207
                'column_info' => 'SELECT `db_name`, `table_name`, `column_name`,'
1208
                    . ' `comment`, `mimetype`, `transformation`,'
1209
                    . ' `transformation_options`, `input_transformation`,'
1210
                    . ' `input_transformation_options` FROM',
1211
                'savedsearches' => 'SELECT `username`, `db_name`, `search_name`, `search_data` FROM',
1212
                default => 'SELECT * FROM ',
1213
            };
1214
1215
            $sqlQuery .= Util::backquote($relationParameters->db)
1216
                . '.' . Util::backquote((string) $relationParams[$type])
1217
                . ' WHERE ' . Util::backquote($dbNameColumn)
1218
                . ' = ' . $dbi->quoteString($db);
1219
            if (isset($table)) {
1220
                $sqlQuery .= ' AND `table_name` = ' . $dbi->quoteString($table);
1221
            }
1222
1223
            if (
1224
                ! $this->exportData(
1225
                    (string) $relationParameters->db,
1226
                    (string) $relationParams[$type],
1227
                    $sqlQuery,
1228
                    $aliases,
1229
                )
1230
            ) {
1231
                return false;
1232
            }
1233
        }
1234
1235
        return true;
1236
    }
1237
1238
    /**
1239
     * Returns a stand-in CREATE definition to resolve view dependencies
1240
     *
1241
     * @param string  $db      the database name
1242
     * @param string  $view    the view name
1243
     * @param mixed[] $aliases Aliases of db/table/columns
1244
     *
1245
     * @return string resulting definition
1246
     */
1247 8
    public function getTableDefStandIn(string $db, string $view, array $aliases = []): string
1248
    {
1249 8
        $dbAlias = $db;
1250 8
        $viewAlias = $view;
1251 8
        $this->initAlias($aliases, $dbAlias, $viewAlias);
1252 8
        $createQuery = '';
1253 8
        if ($this->dropTable) {
1254 4
            $createQuery .= 'DROP VIEW IF EXISTS '
1255 4
                . Util::backquote($viewAlias)
1256 4
                . ';' . "\n";
1257
        }
1258
1259 8
        $createQuery .= 'CREATE TABLE ';
1260
1261 8
        if ($this->ifNotExists) {
1262 4
            $createQuery .= 'IF NOT EXISTS ';
1263
        }
1264
1265 8
        $createQuery .= Util::backquote($viewAlias) . ' (' . "\n";
1266 8
        $tmp = [];
1267 8
        $columns = DatabaseInterface::getInstance()->getColumns($db, $view);
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1267
        $columns = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->getColumns($db, $view);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1268 8
        foreach ($columns as $column) {
1269 8
            $colAlias = $column->field;
1270 8
            if (! empty($aliases[$db]['tables'][$view]['columns'][$colAlias])) {
1271
                $colAlias = $aliases[$db]['tables'][$view]['columns'][$colAlias];
1272
            }
1273
1274 8
            $tmp[] = Util::backquote($colAlias) . ' ' . $column->type . "\n";
1275
        }
1276
1277 8
        return $createQuery . implode(',', $tmp) . ');' . "\n";
1278
    }
1279
1280
    /**
1281
     * Returns CREATE definition that matches $view's structure
1282
     *
1283
     * @param string  $db      the database name
1284
     * @param string  $view    the view name
1285
     * @param mixed[] $aliases Aliases of db/table/columns
1286
     *
1287
     * @return string resulting schema
1288
     */
1289 8
    private function getTableDefForView(
1290
        string $db,
1291
        string $view,
1292
        array $aliases = [],
1293
    ): string {
1294 8
        $dbAlias = $db;
1295 8
        $viewAlias = $view;
1296 8
        $this->initAlias($aliases, $dbAlias, $viewAlias);
1297 8
        $createQuery = 'CREATE TABLE';
1298 8
        if ($this->ifNotExists) {
1299 4
            $createQuery .= ' IF NOT EXISTS ';
1300
        }
1301
1302 8
        $createQuery .= Util::backquote($viewAlias) . '(' . "\n";
1303
1304 8
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1304
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1305 8
        $columns = $dbi->getColumns($db, $view);
1306
1307 8
        $firstCol = true;
1308 8
        foreach ($columns as $column) {
1309 8
            $colAlias = $column->field;
1310 8
            if (! empty($aliases[$db]['tables'][$view]['columns'][$colAlias])) {
1311
                $colAlias = $aliases[$db]['tables'][$view]['columns'][$colAlias];
1312
            }
1313
1314 8
            $extractedColumnspec = Util::extractColumnSpec($column->type);
1315
1316 8
            if (! $firstCol) {
1317 4
                $createQuery .= ',' . "\n";
1318
            }
1319
1320 8
            $createQuery .= '    ' . Util::backquote($colAlias);
1321 8
            $createQuery .= ' ' . $column->type;
1322 8
            if ($extractedColumnspec['can_contain_collation'] && ! empty($column->collation)) {
1323 4
                $createQuery .= ' COLLATE ' . $column->collation;
1324
            }
1325
1326 8
            if (! $column->isNull) {
1327 8
                $createQuery .= ' NOT NULL';
1328
            }
1329
1330 8
            if ($column->default !== null) {
1331 8
                $createQuery .= ' DEFAULT ' . $dbi->quoteString($column->default);
1332 4
            } elseif ($column->isNull) {
1333 4
                $createQuery .= ' DEFAULT NULL';
1334
            }
1335
1336 8
            if ($column->comment !== '') {
1337 4
                $createQuery .= ' COMMENT ' . $dbi->quoteString($column->comment);
1338
            }
1339
1340 8
            $firstCol = false;
1341
        }
1342
1343 8
        $createQuery .= "\n" . ');' . "\n";
1344
1345 8
        if ($this->compatibility === 'MSSQL') {
1346 4
            return $this->makeCreateTableMSSQLCompatible($createQuery);
1347
        }
1348
1349 8
        return $createQuery;
1350
    }
1351
1352
    /**
1353
     * Returns $table's CREATE definition
1354
     *
1355
     * @param string  $db                      the database name
1356
     * @param string  $table                   the table name
1357
     * @param bool    $addSemicolon            whether to add semicolon and
1358
     *                                          end-of-line at the end
1359
     * @param bool    $view                    whether we're handling a view
1360
     * @param bool    $updateIndexesIncrements whether we need to update
1361
     *                                           two global variables
1362
     * @param mixed[] $aliases                 Aliases of db/table/columns
1363
     *
1364
     * @return string resulting schema
1365
     */
1366 12
    public function getTableDef(
1367
        string $db,
1368
        string $table,
1369
        bool $addSemicolon = true,
1370
        bool $view = false,
1371
        bool $updateIndexesIncrements = true,
1372
        array $aliases = [],
1373
    ): string {
1374 12
        $dbAlias = $db;
1375 12
        $tableAlias = $table;
1376 12
        $this->initAlias($aliases, $dbAlias, $tableAlias);
1377
1378 12
        $schemaCreate = $this->getTableStatus($db, $table);
1379
1380 12
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1380
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1381 12
        if ($this->dropTable && $dbi->getTable($db, $table)->isView()) {
1382
            $schemaCreate .= 'DROP VIEW IF EXISTS '
1383
                . Util::backquoteCompat($tableAlias, 'NONE', $this->useSqlBackquotes) . ';'
1384
                . "\n";
1385
        }
1386
1387
        // no need to generate a DROP VIEW here, it was done earlier
1388 12
        if ($this->dropTable && ! $dbi->getTable($db, $table)->isView()) {
1389 8
            $schemaCreate .= 'DROP TABLE IF EXISTS '
1390 8
                . Util::backquoteCompat($tableAlias, 'NONE', $this->useSqlBackquotes) . ';'
1391 8
                . "\n";
1392
        }
1393
1394
        // Complete table dump,
1395
        // Whether to quote table and column names or not
1396 12
        if ($this->useSqlBackquotes) {
1397 8
            $dbi->query('SET SQL_QUOTE_SHOW_CREATE = 1');
1398
        } else {
1399 8
            $dbi->query('SET SQL_QUOTE_SHOW_CREATE = 0');
1400
        }
1401
1402
        // I don't see the reason why this unbuffered query could cause problems,
1403
        // because SHOW CREATE TABLE returns only one row, and we free the
1404
        // results below. Nonetheless, we got 2 user reports about this
1405
        // (see bug 1562533) so I removed the unbuffered mode.
1406
        // $result = $dbi->query('SHOW CREATE TABLE ' . backquote($db)
1407
        // . '.' . backquote($table), null, DatabaseInterface::QUERY_UNBUFFERED);
1408
        //
1409
        // Note: SHOW CREATE TABLE, at least in MySQL 5.1.23, does not
1410
        // produce a displayable result for the default value of a BIT
1411
        // column, nor does the mysqldump command. See MySQL bug 35796
1412 12
        $dbi->tryQuery('USE ' . Util::backquote($db));
1413 12
        $result = $dbi->tryQuery(
1414 12
            'SHOW CREATE TABLE ' . Util::backquote($db) . '.'
1415 12
            . Util::backquote($table),
1416 12
        );
1417
        // an error can happen, for example the table is crashed
1418 12
        $tmpError = $dbi->getError();
1419 12
        if ($tmpError !== '') {
1420 4
            $message = sprintf(__('Error reading structure for table %s:'), $db . '.' . $table);
1421 4
            $message .= ' ' . $tmpError;
1422
1423 4
            throw new ExportException($message);
1424
        }
1425
1426
        // Old mode is stored so it can be restored once exporting is done.
1427 8
        $oldMode = Context::getMode();
1428
1429 8
        $warning = '';
1430
1431 8
        $row = [];
1432 8
        if ($result !== false) {
1433 8
            $row = $result->fetchRow();
1434
        }
1435
1436 8
        if ($row !== []) {
1437 8
            $createQuery = $row[1];
1438 8
            unset($row);
1439
1440
            // Convert end of line chars to one that we want (note that MySQL
1441
            // doesn't return query it will accept in all cases)
1442 8
            if (str_contains($createQuery, "(\r\n ")) {
1443
                $createQuery = str_replace("\r\n", "\n", $createQuery);
1444 8
            } elseif (str_contains($createQuery, "(\n ")) {
1445 8
                $createQuery = str_replace("\n", "\n", $createQuery);
1446
            } elseif (str_contains($createQuery, "(\r ")) {
1447
                $createQuery = str_replace("\r", "\n", $createQuery);
1448
            }
1449
1450
            /**
1451
             * Drop database name from VIEW creation.
1452
             *
1453
             * This is a bit tricky, but we need to issue SHOW CREATE TABLE with
1454
             * database name, but we don't want name to show up in CREATE VIEW
1455
             * statement.
1456
             */
1457 8
            if ($view) {
1458
                //TODO: use parser
1459 4
                $createQuery = preg_replace(
1460 4
                    '/' . preg_quote(Util::backquote($db), '/') . '\./',
1461 4
                    '',
1462 4
                    $createQuery,
1463 4
                );
1464 4
                $parser = new Parser($createQuery);
1465
                /**
1466
                 * `CREATE TABLE` statement.
1467
                 *
1468
                 * @var CreateStatement $statement
1469
                 */
1470 4
                $statement = $parser->statements[0];
1471
1472
                // exclude definition of current user
1473
                if (
1474 4
                    Config::getInstance()->settings['Export']['remove_definer_from_definitions']
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1474
                    /** @scrutinizer ignore-deprecated */ Config::getInstance()->settings['Export']['remove_definer_from_definitions']

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1475 4
                    || $this->viewCurrentUser
1476
                ) {
1477
                    $statement->options->remove('DEFINER');
0 ignored issues
show
Bug introduced by
The method remove() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1477
                    $statement->options->/** @scrutinizer ignore-call */ 
1478
                                         remove('DEFINER');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1478
                }
1479
1480 4
                if ($this->simpleViewExport) {
1481
                    $statement->options->remove('SQL SECURITY');
1482
                    $statement->options->remove('INVOKER');
1483
                    $statement->options->remove('ALGORITHM');
1484
                    $statement->options->remove('DEFINER');
1485
                }
1486
1487 4
                $createQuery = $statement->build();
1488
1489
                // whether to replace existing view or not
1490 4
                if ($this->orReplaceView) {
1491
                    $createQuery = preg_replace('/^CREATE/', 'CREATE OR REPLACE', $createQuery);
1492
                }
1493
            }
1494
1495
            // Substitute aliases in `CREATE` query.
1496 8
            $flag = false;
1497 8
            $createQuery = $this->replaceWithAliases('', $createQuery, $aliases, $db, $flag);
1498
1499
            // One warning per view.
1500 8
            if ($flag && $view) {
1501
                $warning = $this->exportComment()
1502
                    . $this->exportComment(
1503
                        __('It appears your database uses views;'),
1504
                    )
1505
                    . $this->exportComment(
1506
                        __('alias export may not work reliably in all cases.'),
1507
                    )
1508
                    . $this->exportComment();
1509
            }
1510
1511
            // Adding IF NOT EXISTS, if required.
1512 8
            if ($this->ifNotExists) {
1513
                $createQuery = (string) preg_replace('/^CREATE TABLE/', 'CREATE TABLE IF NOT EXISTS', $createQuery);
1514
            }
1515
1516
            // Making the query MSSQL compatible.
1517 8
            if ($this->compatibility === 'MSSQL') {
1518 8
                $createQuery = $this->makeCreateTableMSSQLCompatible($createQuery);
1519
            }
1520
1521
            // Views have no constraints, indexes, etc. They do not require any
1522
            // analysis.
1523 8
            if (! $view) {
1524 8
                if (! $this->useSqlBackquotes) {
1525
                    // Option "Enclose table and column names with backquotes"
1526
                    // was checked.
1527
                    Context::setMode(Context::getMode() | Context::SQL_MODE_NO_ENCLOSING_QUOTES);
1528
                }
1529
1530
                // Using appropriate quotes.
1531 8
                if ($this->compatibility === 'MSSQL') {
1532 8
                    Context::setMode(Context::getMode() | Context::SQL_MODE_ANSI_QUOTES);
1533
                }
1534
            }
1535
1536
            /**
1537
             * Parser used for analysis.
1538
             */
1539 8
            $parser = new Parser($createQuery);
1540
1541
            /**
1542
             * `CREATE TABLE` statement.
1543
             *
1544
             * @var CreateStatement $statement
1545
             */
1546 8
            $statement = $parser->statements[0];
1547
1548 8
            if (! empty($statement->entityOptions)) {
1549 8
                $engine = $statement->entityOptions->get('ENGINE');
1550
            } else {
1551
                $engine = '';
1552
            }
1553
1554
            /* Avoid operation on ARCHIVE tables as those can not be altered */
1555
            if (
1556 8
                ! empty($statement->fields) && is_array($statement->fields)
1557 8
                && ($engine === '' || strtoupper((string) $engine) !== 'ARCHIVE')
1558
            ) {
1559
1560
                /**
1561
                 * Fragments containing definition of each constraint.
1562
                 */
1563 8
                $constraints = [];
1564
1565
                /**
1566
                 * Fragments containing definition of each index.
1567
                 */
1568 8
                $indexes = [];
1569
1570
                /**
1571
                 * Fragments containing definition of each FULLTEXT index.
1572
                 */
1573 8
                $indexesFulltext = [];
1574
1575
                /**
1576
                 * Fragment containing definition of the `AUTO_INCREMENT`.
1577
                 */
1578 8
                $autoIncrement = [];
1579
1580
                // Scanning each field of the `CREATE` statement to fill the arrays
1581
                // above.
1582
                // If the field is used in any of the arrays above, it is removed
1583
                // from the original definition.
1584
                // Also, AUTO_INCREMENT attribute is removed.
1585
                /** @var CreateDefinition $field */
1586 8
                foreach ($statement->fields as $key => $field) {
1587 8
                    if ($field->isConstraint) {
1588
                        // Creating the parts that add constraints.
1589 4
                        $constraints[] = $field->build();
1590 4
                        unset($statement->fields[$key]);
1591 8
                    } elseif ($field->key !== null) {
1592
                        // Creating the parts that add indexes (must not be
1593
                        // constraints).
1594 8
                        if ($field->key->type === 'FULLTEXT KEY') {
1595
                            $indexesFulltext[] = $field->build();
1596
                            unset($statement->fields[$key]);
1597 8
                        } elseif (! $this->ifNotExists) {
1598 8
                            $indexes[] = str_replace(
1599 8
                                'COMMENT=\'',
1600 8
                                'COMMENT \'',
1601 8
                                $field->build(),
1602 8
                            );
1603 8
                            unset($statement->fields[$key]);
1604
                        }
1605
                    }
1606
1607
                    // Creating the parts that drop foreign keys.
1608 8
                    if ($field->key !== null && $field->key->type === 'FOREIGN KEY' && $field->name !== null) {
1609 4
                        unset($statement->fields[$key]);
1610
                    }
1611
1612
                    // Dropping AUTO_INCREMENT.
1613 8
                    if ($field->options === null) {
1614 8
                        continue;
1615
                    }
1616
1617 8
                    if (! $field->options->has('AUTO_INCREMENT') || $this->ifNotExists) {
1618 8
                        continue;
1619
                    }
1620
1621 8
                    $autoIncrement[] = $field->build();
1622 8
                    $field->options->remove('AUTO_INCREMENT');
1623
                }
1624
1625
                /**
1626
                 * The header of the `ALTER` statement (`ALTER TABLE tbl`).
1627
                 */
1628 8
                $alterHeader = 'ALTER TABLE ' . Util::backquoteCompat(
1629 8
                    $tableAlias,
1630 8
                    $this->compatibility,
1631 8
                    $this->useSqlBackquotes,
1632 8
                );
1633
1634
                /**
1635
                 * The footer of the `ALTER` statement (usually ';')
1636
                 */
1637 8
                $alterFooter = ';' . "\n";
1638
1639
                // Generating constraints-related query.
1640 8
                if ($constraints !== []) {
1641 4
                    $this->sqlConstraintsQuery = $alterHeader . "\n" . '  ADD '
1642 4
                        . implode(',' . "\n" . '  ADD ', $constraints)
1643 4
                        . $alterFooter;
1644
1645 4
                    $this->sqlConstraints = $this->generateComment(
1646 4
                        $this->sqlConstraints,
1647 4
                        __('Constraints for dumped tables'),
1648 4
                        __('Constraints for table'),
1649 4
                        $tableAlias,
1650 4
                        $this->compatibility,
1651 4
                    ) . $this->sqlConstraintsQuery;
1652
                }
1653
1654
                // Generating indexes-related query.
1655 8
                $indexesQuery = '';
1656 8
                if ($indexes !== []) {
1657 8
                    $indexesQuery .= $alterHeader . "\n" . '  ADD '
1658 8
                        . implode(',' . "\n" . '  ADD ', $indexes)
1659 8
                        . $alterFooter;
1660
                }
1661
1662 8
                if ($indexesFulltext !== []) {
1663
                    // InnoDB supports one FULLTEXT index creation at a time.
1664
                    // So FULLTEXT indexes are created one-by-one after other
1665
                    // indexes where created.
1666
                    $indexesQuery .= $alterHeader
1667
                        . ' ADD ' . implode($alterFooter . $alterHeader . ' ADD ', $indexesFulltext)
1668
                        . $alterFooter;
1669
                }
1670
1671 8
                if ($indexes !== [] || $indexesFulltext !== []) {
1672 8
                    $this->sqlIndexes = $this->generateComment(
1673 8
                        $this->sqlIndexes,
1674 8
                        __('Indexes for dumped tables'),
1675 8
                        __('Indexes for table'),
1676 8
                        $tableAlias,
1677 8
                        $this->compatibility,
1678 8
                    ) . $indexesQuery;
1679
                }
1680
1681
                // Generating auto-increment-related query.
1682 8
                if ($autoIncrement !== [] && $updateIndexesIncrements) {
1683 8
                    $sqlAutoIncrementsQuery = $alterHeader . "\n" . '  MODIFY '
1684 8
                        . implode(',' . "\n" . '  MODIFY ', $autoIncrement);
1685
                    if (
1686 8
                        $this->autoIncrement
1687 8
                        && $statement->entityOptions->has('AUTO_INCREMENT')
0 ignored issues
show
Bug introduced by
The method has() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1687
                        && $statement->entityOptions->/** @scrutinizer ignore-call */ has('AUTO_INCREMENT')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1688 8
                        && (Export::$tableData === [] || in_array($table, Export::$tableData, true))
1689
                    ) {
1690
                        $sqlAutoIncrementsQuery .= ', AUTO_INCREMENT='
1691
                            . $statement->entityOptions->get('AUTO_INCREMENT');
1692
                    }
1693
1694 8
                    $sqlAutoIncrementsQuery .= ';' . "\n";
1695
1696 8
                    $this->sqlAutoIncrements = $this->generateComment(
1697 8
                        $this->sqlAutoIncrements,
1698 8
                        __('AUTO_INCREMENT for dumped tables'),
1699 8
                        __('AUTO_INCREMENT for table'),
1700 8
                        $tableAlias,
1701 8
                        $this->compatibility,
1702 8
                    ) . $sqlAutoIncrementsQuery;
1703
                }
1704
1705
                // Removing the `AUTO_INCREMENT` attribute from the `CREATE TABLE`
1706
                // too.
1707
                if (
1708 8
                    $statement->entityOptions !== null
1709 8
                    && (! $this->ifNotExists || ! $this->autoIncrement)
1710
                ) {
1711 8
                    $statement->entityOptions->remove('AUTO_INCREMENT');
1712
                }
1713
1714
                // Rebuilding the query.
1715 8
                $createQuery = $statement->build();
1716
            }
1717
1718 8
            $schemaCreate .= $createQuery;
1719
        }
1720
1721
        // Restoring old mode.
1722 8
        Context::setMode($oldMode);
1723
1724 8
        return $warning . $schemaCreate . ($addSemicolon ? ';' . "\n" : '');
1725
    }
1726
1727
    /**
1728
     * Returns $table's comments, relations etc.
1729
     *
1730
     * @param string  $db      database name
1731
     * @param string  $table   table name
1732
     * @param mixed[] $aliases Aliases of db/table/columns
1733
     *
1734
     * @return string resulting comments
1735
     */
1736 8
    private function getTableComments(string $db, string $table, array $aliases = []): string
1737
    {
1738 8
        $dbAlias = $db;
1739 8
        $tableAlias = $table;
1740 8
        $this->initAlias($aliases, $dbAlias, $tableAlias);
1741
1742 8
        $relationParameters = $this->relation->getRelationParameters();
1743
1744 8
        $schemaCreate = '';
1745
1746 8
        $mimeMap = null;
1747 8
        if ($this->doMime && $relationParameters->browserTransformationFeature !== null) {
1748 4
            $mimeMap = $this->transformations->getMime($db, $table, true);
1749
        }
1750
1751 8
        if ($mimeMap !== null && $mimeMap !== []) {
1752 4
            $schemaCreate .= $this->possibleCRLF()
1753 4
                . $this->exportComment()
1754 4
                . $this->exportComment(
1755 4
                    __('MEDIA TYPES FOR TABLE') . ' '
1756 4
                    . Util::backquoteCompat($table, 'NONE', $this->useSqlBackquotes) . ':',
1757 4
                );
1758 4
            foreach ($mimeMap as $mimeField => $mime) {
1759 4
                $schemaCreate .= $this->exportComment(
1760 4
                    '  '
1761 4
                    . Util::backquoteCompat($mimeField, 'NONE', $this->useSqlBackquotes),
1762 4
                )
1763 4
                . $this->exportComment(
1764 4
                    '      '
1765 4
                    . Util::backquoteCompat(
1766 4
                        $mime['mimetype'],
1767 4
                        'NONE',
1768 4
                        $this->useSqlBackquotes,
1769 4
                    ),
1770 4
                );
1771
            }
1772
1773 4
            $schemaCreate .= $this->exportComment();
1774
        }
1775
1776
        // Check if we can use Relations
1777 8
        $foreigners = $this->doRelation && $relationParameters->relationFeature !== null ?
1778 4
            $this->relation->getForeignersInternal($db, $table)
1779 4
            : [];
1780
1781 8
        if ($foreigners !== []) {
1782 4
            $schemaCreate .= $this->possibleCRLF()
1783 4
                . $this->exportComment()
1784 4
                . $this->exportComment(
1785 4
                    __('RELATIONSHIPS FOR TABLE') . ' '
1786 4
                    . Util::backquoteCompat($tableAlias, 'NONE', $this->useSqlBackquotes)
1787 4
                    . ':',
1788 4
                );
1789
1790 4
            foreach ($foreigners as $relField => $rel) {
1791 4
                $relFieldAlias = ! empty(
1792 4
                    $aliases[$db]['tables'][$table]['columns'][$relField]
1793
                ) ? $aliases[$db]['tables'][$table]['columns'][$relField]
1794 4
                    : $relField;
1795 4
                $schemaCreate .= $this->exportComment(
1796 4
                    '  '
1797 4
                    . Util::backquoteCompat(
1798 4
                        $relFieldAlias,
1799 4
                        'NONE',
1800 4
                        $this->useSqlBackquotes,
1801 4
                    ),
1802 4
                )
1803 4
                . $this->exportComment(
1804 4
                    '      '
1805 4
                    . Util::backquoteCompat(
1806 4
                        $rel['foreign_table'],
1807 4
                        'NONE',
1808 4
                        $this->useSqlBackquotes,
1809 4
                    )
1810 4
                    . ' -> '
1811 4
                    . Util::backquoteCompat(
1812 4
                        $rel['foreign_field'],
1813 4
                        'NONE',
1814 4
                        $this->useSqlBackquotes,
1815 4
                    ),
1816 4
                );
1817
            }
1818
1819 4
            foreach ($this->relation->getForeignKeysData($db, $table) as $oneKey) {
1820
                foreach ($oneKey->indexList as $index => $field) {
1821
                    $relFieldAlias = ! empty(
1822
                        $aliases[$db]['tables'][$table]['columns'][$field]
1823
                    ) ? $aliases[$db]['tables'][$table]['columns'][$field]
1824
                        : $field;
1825
                    $schemaCreate .= $this->exportComment(
1826
                        '  '
1827
                        . Util::backquoteCompat(
1828
                            $relFieldAlias,
1829
                            'NONE',
1830
                            $this->useSqlBackquotes,
1831
                        ),
1832
                    )
1833
                    . $this->exportComment(
1834
                        '      '
1835
                        . Util::backquoteCompat(
1836
                            $oneKey->refTableName,
1837
                            'NONE',
1838
                            $this->useSqlBackquotes,
1839
                        )
1840
                        . ' -> '
1841
                        . Util::backquoteCompat(
1842
                            $oneKey->refIndexList[$index],
1843
                            'NONE',
1844
                            $this->useSqlBackquotes,
1845
                        ),
1846
                    );
1847
                }
1848
            }
1849
1850 4
            $schemaCreate .= $this->exportComment();
1851
        }
1852
1853 8
        return $schemaCreate;
1854
    }
1855
1856
    /**
1857
     * Outputs a raw query
1858
     *
1859
     * @param string|null $db       the database where the query is executed
1860
     * @param string      $sqlQuery the rawquery to output
1861
     */
1862
    public function exportRawQuery(string|null $db, string $sqlQuery): bool
1863
    {
1864
        if ($db !== null) {
1865
            DatabaseInterface::getInstance()->selectDb($db);
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1865
            /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->selectDb($db);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1866
        }
1867
1868
        return $this->exportData($db ?? '', '', $sqlQuery);
1869
    }
1870
1871
    /**
1872
     * Outputs table's structure
1873
     *
1874
     * @param string  $db         database name
1875
     * @param string  $table      table name
1876
     * @param string  $exportMode 'create_table', 'triggers', 'create_view', 'stand_in'
1877
     * @param mixed[] $aliases    Aliases of db/table/columns
1878
     */
1879 4
    public function exportStructure(string $db, string $table, string $exportMode, array $aliases = []): bool
1880
    {
1881 4
        $dbAlias = $db;
1882 4
        $tableAlias = $table;
1883 4
        $this->initAlias($aliases, $dbAlias, $tableAlias);
1884 4
        $formattedTableName = Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes);
1885 4
        $dump = $this->possibleCRLF()
1886 4
            . $this->exportComment(str_repeat('-', 56))
1887 4
            . $this->possibleCRLF()
1888 4
            . $this->exportComment();
1889
1890
        switch ($exportMode) {
1891 4
            case 'create_table':
1892 4
                $dump .= $this->exportComment(
1893 4
                    __('Table structure for table') . ' ' . $formattedTableName,
1894 4
                );
1895 4
                $dump .= $this->exportComment();
1896 4
                $dump .= $this->getTableDef($db, $table, true, false, true, $aliases);
1897 4
                $dump .= $this->getTableComments($db, $table, $aliases);
1898 4
                break;
1899 4
            case 'triggers':
1900 4
                $dump = '';
1901 4
                $delimiter = '$$';
1902 4
                $triggers = Triggers::getDetails(DatabaseInterface::getInstance(), $db, $table);
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1902
                $triggers = Triggers::getDetails(/** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance(), $db, $table);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1903 4
                if ($triggers !== []) {
1904 4
                    $dump .= $this->possibleCRLF()
1905 4
                    . $this->exportComment()
1906 4
                    . $this->exportComment(
1907 4
                        __('Triggers') . ' ' . $formattedTableName,
1908 4
                    )
1909 4
                        . $this->exportComment();
1910 4
                    $usedAlias = false;
1911 4
                    $triggerQuery = '';
1912 4
                    foreach ($triggers as $trigger) {
1913 4
                        if ($this->dropTable) {
1914
                            $triggerQuery .= $trigger->getDropSql() . ';' . "\n";
1915
                        }
1916
1917 4
                        $flag = false;
1918 4
                        $triggerQuery .= 'DELIMITER ' . $delimiter . "\n";
1919 4
                        $triggerQuery .= $this->replaceWithAliases(
1920 4
                            $delimiter,
1921 4
                            $trigger->getCreateSql($delimiter),
1922 4
                            $aliases,
1923 4
                            $db,
1924 4
                            $flag,
1925 4
                        );
1926 4
                        if ($flag) {
1927
                            $usedAlias = true;
1928
                        }
1929
1930 4
                        $triggerQuery .= $delimiter . "\n" . 'DELIMITER ;' . "\n";
1931
                    }
1932
1933
                    // One warning per table.
1934 4
                    if ($usedAlias) {
1935
                        $dump .= $this->exportComment(
1936
                            __('It appears your table uses triggers;'),
1937
                        )
1938
                        . $this->exportComment(
1939
                            __('alias export may not work reliably in all cases.'),
1940
                        )
1941
                        . $this->exportComment();
1942
                    }
1943
1944 4
                    $dump .= $triggerQuery;
1945
                }
1946
1947 4
                break;
1948 4
            case 'create_view':
1949 4
                if (! $this->viewsAsTables) {
1950 4
                    $dump .= $this->exportComment(
1951 4
                        __('Structure for view')
1952 4
                        . ' '
1953 4
                        . $formattedTableName,
1954 4
                    )
1955 4
                    . $this->exportComment();
1956
                    // delete the stand-in table previously created (if any)
1957 4
                    if (ExportPlugin::$exportType !== ExportType::Table) {
1958 4
                        $dump .= 'DROP TABLE IF EXISTS '
1959 4
                            . Util::backquote($tableAlias) . ';' . "\n";
1960
                    }
1961
1962 4
                    $dump .= $this->getTableDef($db, $table, true, true, true, $aliases);
1963
                } else {
1964 4
                    $dump .= $this->exportComment(
1965 4
                        sprintf(
1966 4
                            __('Structure for view %s exported as a table'),
1967 4
                            $formattedTableName,
1968 4
                        ),
1969 4
                    )
1970 4
                    . $this->exportComment();
1971
                    // delete the stand-in table previously created (if any)
1972 4
                    if (ExportPlugin::$exportType !== ExportType::Table) {
1973 4
                        $dump .= 'DROP TABLE IF EXISTS '
1974 4
                        . Util::backquote($tableAlias) . ';' . "\n";
1975
                    }
1976
1977 4
                    $dump .= $this->getTableDefForView($db, $table, $aliases);
1978
                }
1979
1980 4
                if (! $this->viewsAsTables) {
1981
                    // Save views, to be inserted after indexes
1982
                    // in case the view uses USE INDEX syntax
1983 4
                    $this->sqlViews .= $dump;
1984 4
                    $dump = '';
1985
                }
1986
1987 4
                break;
1988 4
            case 'stand_in':
1989 4
                $dump .= $this->exportComment(
1990 4
                    __('Stand-in structure for view') . ' ' . $formattedTableName,
1991 4
                )
1992 4
                    . $this->exportComment(
1993 4
                        __('(See below for the actual view)'),
1994 4
                    )
1995 4
                    . $this->exportComment();
1996
                // export a stand-in definition to resolve view dependencies
1997 4
                $dump .= $this->getTableDefStandIn($db, $table, $aliases);
1998
        }
1999
2000
        // this one is built by getTableDef() to use in table copy/move
2001
        // but not in the case of export
2002 4
        $this->sqlConstraintsQuery = '';
2003
2004 4
        return $this->export->outputHandler($dump);
2005
    }
2006
2007
    /**
2008
     * Outputs the content of a table in SQL format
2009
     *
2010
     * @param string  $db       database name
2011
     * @param string  $table    table name
2012
     * @param string  $sqlQuery SQL query for obtaining data
2013
     * @param mixed[] $aliases  Aliases of db/table/columns
2014
     */
2015 16
    public function exportData(
2016
        string $db,
2017
        string $table,
2018
        string $sqlQuery,
2019
        array $aliases = [],
2020
    ): bool {
2021 16
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2021
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2022
        // Do not export data for merge tables
2023 16
        if ($dbi->getTable($db, $table)->isMerge()) {
2024
            return true;
2025
        }
2026
2027 16
        $dbAlias = $db;
2028 16
        $tableAlias = $table;
2029 16
        $this->initAlias($aliases, $dbAlias, $tableAlias);
2030
2031 16
        $formattedTableName = Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes);
2032
2033
        // Do not export data for a VIEW, unless asked to export the view as a table
2034
        // (For a VIEW, this is called only when exporting a single VIEW)
2035 16
        if ($dbi->getTable($db, $table)->isView() && ! $this->viewsAsTables) {
2036 4
            $head = $this->possibleCRLF()
2037 4
                . $this->exportComment()
2038 4
                . $this->exportComment('VIEW ' . $formattedTableName)
2039 4
                . $this->exportComment(__('Data:') . ' ' . __('None'))
2040 4
                . $this->exportComment()
2041 4
                . $this->possibleCRLF();
2042
2043 4
            return $this->export->outputHandler($head);
2044
        }
2045
2046 12
        $result = $dbi->tryQuery($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED);
2047
        // a possible error: the table has crashed
2048 12
        $tmpError = $dbi->getError();
2049 12
        if ($tmpError !== '') {
2050 4
            $message = sprintf(__('Error reading data for table %s:'), $db . '.' . $table);
2051 4
            $message .= ' ' . $tmpError;
2052
2053 4
            throw new ExportException($message);
2054
        }
2055
2056 8
        if ($result === false) {
2057
            return true;
2058
        }
2059
2060 8
        $fieldsCnt = $result->numFields();
2061
2062
        // Get field information
2063 8
        $fieldsMeta = $dbi->getFieldsMeta($result);
2064
2065 8
        $fieldSet = [];
2066
        /** @infection-ignore-all */
2067 8
        for ($j = 0; $j < $fieldsCnt; $j++) {
2068 8
            $colAs = $fieldsMeta[$j]->name;
2069 8
            if (! empty($aliases[$db]['tables'][$table]['columns'][$colAs])) {
2070
                $colAs = $aliases[$db]['tables'][$table]['columns'][$colAs];
2071
            }
2072
2073 8
            $fieldSet[$j] = Util::backquoteCompat($colAs, $this->compatibility, $this->useSqlBackquotes);
2074
        }
2075
2076 8
        if ($this->type === 'UPDATE') {
2077
            // update
2078 4
            $schemaInsert = 'UPDATE ';
2079 4
            if ($this->ignore) {
2080 4
                $schemaInsert .= 'IGNORE ';
2081
            }
2082
2083
            // avoid EOL blank
2084 4
            $schemaInsert .= Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes) . ' SET';
2085
        } else {
2086
            // insert or replace
2087 4
            if ($this->type === 'REPLACE') {
2088
                $sqlCommand = 'REPLACE';
2089
            } else {
2090 4
                $sqlCommand = 'INSERT';
2091
            }
2092
2093
            // delayed inserts?
2094 4
            if ($this->delayed) {
2095 4
                $insertDelayed = ' DELAYED';
2096
            } else {
2097
                $insertDelayed = '';
2098
            }
2099
2100
            // insert ignore?
2101 4
            if ($this->ignore && $this->type === 'INSERT') {
2102 4
                $insertDelayed .= ' IGNORE';
2103
            }
2104
2105
            //truncate table before insert
2106 4
            if ($this->truncate && $sqlCommand === 'INSERT') {
2107 4
                $truncate = 'TRUNCATE TABLE '
2108 4
                    . Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes) . ';';
2109 4
                $truncatehead = $this->possibleCRLF()
2110 4
                    . $this->exportComment()
2111 4
                    . $this->exportComment(
2112 4
                        __('Truncate table before insert') . ' '
2113 4
                        . $formattedTableName,
2114 4
                    )
2115 4
                    . $this->exportComment()
2116 4
                    . "\n";
2117 4
                $this->export->outputHandler($truncatehead);
2118 4
                $this->export->outputHandler($truncate);
2119
            }
2120
2121
            // scheme for inserting fields
2122 4
            if ($this->insertSyntax === 'complete' || $this->insertSyntax === 'both') {
2123 4
                $fields = implode(', ', $fieldSet);
2124 4
                $schemaInsert = $sqlCommand . $insertDelayed . ' INTO '
2125 4
                    . Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes)
2126 4
                    . ' (' . $fields . ') VALUES'; // avoid EOL blank
2127
            } else {
2128
                $schemaInsert = $sqlCommand . $insertDelayed . ' INTO '
2129
                    . Util::backquoteCompat($tableAlias, $this->compatibility, $this->useSqlBackquotes)
2130
                    . ' VALUES';
2131
            }
2132
        }
2133
2134
        //\x08\\x09, not required
2135 8
        $currentRow = 0;
2136 8
        $querySize = 0;
2137 8
        if (($this->insertSyntax === 'extended' || $this->insertSyntax === 'both') && $this->type !== 'UPDATE') {
2138 4
            $separator = ',';
2139 4
            $schemaInsert .= "\n";
2140
        } else {
2141 4
            $separator = ';';
2142
        }
2143
2144 8
        while ($row = $result->fetchRow()) {
2145 8
            if ($currentRow === 0) {
2146 8
                $head = $this->possibleCRLF()
2147 8
                    . $this->exportComment()
2148 8
                    . $this->exportComment(
2149 8
                        __('Dumping data for table') . ' '
2150 8
                        . $formattedTableName,
2151 8
                    )
2152 8
                    . $this->exportComment()
2153 8
                    . "\n";
2154 8
                if (! $this->export->outputHandler($head)) {
2155
                    return false;
2156
                }
2157
            }
2158
2159
            // We need to SET IDENTITY_INSERT ON for MSSQL
2160 8
            if ($currentRow === 0 && $this->compatibility === 'MSSQL') {
2161
                if (
2162 8
                    ! $this->export->outputHandler(
2163 8
                        'SET IDENTITY_INSERT '
2164 8
                        . Util::backquoteCompat(
2165 8
                            $tableAlias,
2166 8
                            $this->compatibility,
2167 8
                            $this->useSqlBackquotes,
2168 8
                        )
2169 8
                        . ' ON ;' . "\n",
2170 8
                    )
2171
                ) {
2172
                    return false;
2173
                }
2174
            }
2175
2176 8
            $currentRow++;
2177 8
            $values = [];
2178 8
            foreach ($fieldsMeta as $j => $metaInfo) {
2179
                // NULL
2180 8
                if ($row[$j] === null) {
2181 8
                    $values[] = 'NULL';
2182
                } elseif (
2183 4
                    $metaInfo->isNumeric
2184
                ) {
2185
                    // a number
2186
                    $values[] = $row[$j];
2187 4
                } elseif ($metaInfo->isBinary && $this->hexForBinary) {
2188
                    // a true BLOB
2189
                    // - mysqldump only generates hex data when the --hex-blob
2190
                    //   option is used, for fields having the binary attribute
2191
                    //   no hex is generated
2192
                    // - a TEXT field returns type blob but a real blob
2193
                    //   returns also the 'binary' flag
2194
2195
                    // empty blobs need to be different, but '0' is also empty
2196
                    // :-(
2197 4
                    if (empty($row[$j]) && $row[$j] != '0') {
2198
                        $values[] = '\'\'';
2199
                    } else {
2200 4
                        $values[] = '0x' . bin2hex($row[$j]);
2201
                    }
2202 4
                } elseif ($metaInfo->isMappedTypeBit) {
2203
                    // detection of 'bit' works only on mysqli extension
2204
                    $values[] = "b'" . Util::printableBitValue((int) $row[$j], $metaInfo->length) . "'";
2205 4
                } elseif ($metaInfo->isMappedTypeGeometry) {
2206
                    // export GIS types as hex
2207
                    $values[] = '0x' . bin2hex($row[$j]);
0 ignored issues
show
Bug introduced by
It seems like $row[$j] can also be of type null; however, parameter $string of bin2hex() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2207
                    $values[] = '0x' . bin2hex(/** @scrutinizer ignore-type */ $row[$j]);
Loading history...
2208 4
                } elseif (self::$exportingMetadata && $row[$j] === '@LAST_PAGE') {
2209
                    $values[] = '@LAST_PAGE';
2210 4
                } elseif ($row[$j] === '') {
2211
                    $values[] = "''";
2212
                } else {
2213
                    // something else -> treat as a string
2214 4
                    $values[] = $dbi->quoteString($row[$j]);
0 ignored issues
show
Bug introduced by
It seems like $row[$j] can also be of type null; however, parameter $str of PhpMyAdmin\Dbal\DatabaseInterface::quoteString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2214
                    $values[] = $dbi->quoteString(/** @scrutinizer ignore-type */ $row[$j]);
Loading history...
2215
                }
2216
            }
2217
2218
            // should we make update?
2219 8
            if ($this->type === 'UPDATE') {
2220 4
                $insertLine = $schemaInsert;
2221
                /** @infection-ignore-all */
2222 4
                for ($i = 0; $i < $fieldsCnt; $i++) {
2223 4
                    if ($i === 0) {
2224 4
                        $insertLine .= ' ';
2225
                    }
2226
2227 4
                    if ($i > 0) {
2228
                        // avoid EOL blank
2229 4
                        $insertLine .= ',';
2230
                    }
2231
2232 4
                    $insertLine .= $fieldSet[$i] . ' = ' . $values[$i];
2233
                }
2234
2235 4
                $insertLine .= ' WHERE ' . (new UniqueCondition($fieldsMeta, $row))->getWhereClause();
2236 4
            } elseif ($this->insertSyntax === 'extended' || $this->insertSyntax === 'both') {
2237
                // Extended inserts case
2238 4
                if ($currentRow === 1) {
2239 4
                    $insertLine = $schemaInsert . '('
2240 4
                        . implode(', ', $values) . ')';
2241
                } else {
2242
                    $insertLine = '(' . implode(', ', $values) . ')';
2243
                    $insertLineSize = mb_strlen($insertLine);
2244
                    if ($this->maxQuerySize > 0 && $querySize + $insertLineSize > $this->maxQuerySize) {
2245
                        if (! $this->export->outputHandler(';' . "\n")) {
2246
                            return false;
2247
                        }
2248
2249
                        $querySize = 0;
2250
                        $currentRow = 1;
2251
                        $insertLine = $schemaInsert . $insertLine;
2252
                    }
2253
                }
2254
2255 4
                $querySize += mb_strlen($insertLine);
2256
            } else {
2257
                // Other inserts case
2258
                $insertLine = $schemaInsert . '(' . implode(', ', $values) . ')';
2259
            }
2260
2261 8
            if (! $this->export->outputHandler(($currentRow === 1 ? '' : $separator . "\n") . $insertLine)) {
2262
                return false;
2263
            }
2264
        }
2265
2266 8
        if ($currentRow > 0) {
2267 8
            if (! $this->export->outputHandler(';' . "\n")) {
2268
                return false;
2269
            }
2270
        }
2271
2272
        // We need to SET IDENTITY_INSERT OFF for MSSQL
2273 8
        if ($this->compatibility === 'MSSQL' && $currentRow > 0) {
2274 8
            $outputSucceeded = $this->export->outputHandler(
2275 8
                "\n" . 'SET IDENTITY_INSERT '
2276 8
                . Util::backquoteCompat(
2277 8
                    $tableAlias,
2278 8
                    $this->compatibility,
2279 8
                    $this->useSqlBackquotes,
2280 8
                )
2281 8
                . ' OFF;' . "\n",
2282 8
            );
2283 8
            if (! $outputSucceeded) {
2284
                return false;
2285
            }
2286
        }
2287
2288 8
        return true;
2289
    }
2290
2291
    /**
2292
     * Make a create table statement compatible with MSSQL
2293
     *
2294
     * @param string $createQuery MySQL create table statement
2295
     *
2296
     * @return string MSSQL compatible create table statement
2297
     */
2298 16
    private function makeCreateTableMSSQLCompatible(string $createQuery): string
2299
    {
2300
        // In MSSQL
2301
        // 1. No 'IF NOT EXISTS' in CREATE TABLE
2302
        // 2. DATE field doesn't exists, we will use DATETIME instead
2303
        // 3. UNSIGNED attribute doesn't exist
2304
        // 4. No length on INT, TINYINT, SMALLINT, BIGINT and no precision on
2305
        //    FLOAT fields
2306
        // 5. No KEY and INDEX inside CREATE TABLE
2307
        // 6. DOUBLE field doesn't exists, we will use FLOAT instead
2308
2309 16
        $createQuery = (string) preg_replace('/^CREATE TABLE IF NOT EXISTS/', 'CREATE TABLE', $createQuery);
2310
        // first we need  to replace all lines ended with '" DATE ...,\n'
2311
        // last preg_replace preserve us from situation with date text
2312
        // inside DEFAULT field value
2313 16
        $createQuery = (string) preg_replace(
2314 16
            "/\" date DEFAULT NULL(,)?\n/",
2315 16
            '" datetime DEFAULT NULL$1' . "\n",
2316 16
            $createQuery,
2317 16
        );
2318 16
        $createQuery = (string) preg_replace("/\" date NOT NULL(,)?\n/", '" datetime NOT NULL$1' . "\n", $createQuery);
2319 16
        $createQuery = (string) preg_replace(
2320 16
            '/" date NOT NULL DEFAULT \'([^\'])/',
2321 16
            '" datetime NOT NULL DEFAULT \'$1',
2322 16
            $createQuery,
2323 16
        );
2324
2325
        // next we need to replace all lines ended with ') UNSIGNED ...,'
2326
        // last preg_replace preserve us from situation with unsigned text
2327
        // inside DEFAULT field value
2328 16
        $createQuery = (string) preg_replace("/\) unsigned NOT NULL(,)?\n/", ') NOT NULL$1' . "\n", $createQuery);
2329 16
        $createQuery = (string) preg_replace(
2330 16
            "/\) unsigned DEFAULT NULL(,)?\n/",
2331 16
            ') DEFAULT NULL$1' . "\n",
2332 16
            $createQuery,
2333 16
        );
2334 16
        $createQuery = (string) preg_replace(
2335 16
            '/\) unsigned NOT NULL DEFAULT \'([^\'])/',
2336 16
            ') NOT NULL DEFAULT \'$1',
2337 16
            $createQuery,
2338 16
        );
2339
2340
        // we need to replace all lines ended with
2341
        // '" INT|TINYINT([0-9]{1,}) ...,' last preg_replace preserve us
2342
        // from situation with int([0-9]{1,}) text inside DEFAULT field
2343
        // value
2344 16
        $createQuery = (string) preg_replace(
2345 16
            '/" (int|tinyint|smallint|bigint)\([0-9]+\) DEFAULT NULL(,)?\n/',
2346 16
            '" $1 DEFAULT NULL$2' . "\n",
2347 16
            $createQuery,
2348 16
        );
2349 16
        $createQuery = (string) preg_replace(
2350 16
            '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL(,)?\n/',
2351 16
            '" $1 NOT NULL$2' . "\n",
2352 16
            $createQuery,
2353 16
        );
2354 16
        $createQuery = (string) preg_replace(
2355 16
            '/" (int|tinyint|smallint|bigint)\([0-9]+\) NOT NULL DEFAULT \'([^\'])/',
2356 16
            '" $1 NOT NULL DEFAULT \'$2',
2357 16
            $createQuery,
2358 16
        );
2359
2360
        // we need to replace all lines ended with
2361
        // '" FLOAT|DOUBLE([0-9,]{1,}) ...,'
2362
        // last preg_replace preserve us from situation with
2363
        // float([0-9,]{1,}) text inside DEFAULT field value
2364 16
        $createQuery = (string) preg_replace(
2365 16
            '/" (float|double)(\([0-9]+,[0-9,]+\))? DEFAULT NULL(,)?\n/',
2366 16
            '" float DEFAULT NULL$3' . "\n",
2367 16
            $createQuery,
2368 16
        );
2369 16
        $createQuery = (string) preg_replace(
2370 16
            '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL(,)?\n/',
2371 16
            '" float NOT NULL$3' . "\n",
2372 16
            $createQuery,
2373 16
        );
2374
2375 16
        return (string) preg_replace(
2376 16
            '/" (float|double)(\([0-9,]+,[0-9,]+\))? NOT NULL DEFAULT \'([^\'])/',
2377 16
            '" float NOT NULL DEFAULT \'$3',
2378 16
            $createQuery,
2379 16
        );
2380
2381
        // @todo remove indexes from CREATE TABLE
2382
    }
2383
2384
    /**
2385
     * replaces db/table/column names with their aliases
2386
     *
2387
     * @param string  $delimiter The delimiter for the parser (";" or "$$")
2388
     * @param string  $sqlQuery  SQL query in which aliases are to be substituted
2389
     * @param mixed[] $aliases   Alias information for db/table/column
2390
     * @param string  $db        the database name
2391
     * @param bool    $flag      the flag denoting whether any replacement was done
2392
     *
2393
     * @return string query replaced with aliases
2394
     */
2395 16
    public function replaceWithAliases(
2396
        string $delimiter,
2397
        string $sqlQuery,
2398
        array $aliases,
2399
        string $db,
2400
        bool &$flag,
2401
    ): string {
2402 16
        $flag = false;
2403
2404
        /**
2405
         * The parser of this query.
2406
         */
2407 16
        $parser = new Parser($delimiter === '' ? $sqlQuery : 'DELIMITER ' . $delimiter . "\n" . $sqlQuery);
2408
2409 16
        if (empty($parser->statements[0])) {
2410
            return $sqlQuery;
2411
        }
2412
2413
        /**
2414
         * The statement that represents the query.
2415
         *
2416
         * @var CreateStatement $statement
2417
         */
2418 16
        $statement = $parser->statements[0];
2419
2420
        /**
2421
         * Old database name.
2422
         */
2423 16
        $oldDatabase = $db;
2424
2425
        // Replacing aliases in `CREATE TABLE` statement.
2426 16
        if ($statement->options->has('TABLE')) {
2427
            // Extracting the name of the old database and table from the
2428
            // statement to make sure the parameters are correct.
2429 12
            if (! empty($statement->name->database)) {
2430
                $oldDatabase = $statement->name->database;
2431
            }
2432
2433
            /**
2434
             * Old table name.
2435
             */
2436 12
            $oldTable = $statement->name->table;
2437
2438
            // Finding the aliased database name.
2439
            // The database might be empty so we have to add a few checks.
2440 12
            $newDatabase = null;
2441 12
            if (! empty($statement->name->database)) {
2442
                $newDatabase = $statement->name->database;
2443
                if (! empty($aliases[$oldDatabase]['alias'])) {
2444
                    $newDatabase = $aliases[$oldDatabase]['alias'];
2445
                }
2446
            }
2447
2448
            // Finding the aliases table name.
2449 12
            $newTable = $oldTable;
2450 12
            if (! empty($aliases[$oldDatabase]['tables'][$oldTable]['alias'])) {
2451 4
                $newTable = $aliases[$oldDatabase]['tables'][$oldTable]['alias'];
2452
            }
2453
2454
            // Replacing new values.
2455 12
            if ($statement->name->database !== $newDatabase || $statement->name->table !== $newTable) {
2456 4
                $statement->name->database = $newDatabase;
2457 4
                $statement->name->table = $newTable;
2458 4
                $statement->name->expr = ''; // Force rebuild.
2459 4
                $flag = true;
2460
            }
2461
2462
            /** @var CreateDefinition[] $fields */
2463 12
            $fields = $statement->fields;
2464 12
            foreach ($fields as $field) {
2465
                // Column name.
2466
                if (
2467 12
                    $field->type !== null
2468 12
                    && ! empty($aliases[$oldDatabase]['tables'][$oldTable]['columns'][$field->name])
2469
                ) {
2470 4
                    $field->name = $aliases[$oldDatabase]['tables'][$oldTable]['columns'][$field->name];
2471 4
                    $flag = true;
2472
                }
2473
2474
                // Key's columns.
2475 12
                if ($field->key !== null) {
2476 12
                    foreach ($field->key->columns as $key => $column) {
2477 12
                        if (! isset($column['name'])) {
2478
                            // In case the column has no name field
2479
                            continue;
2480
                        }
2481
2482 12
                        if (empty($aliases[$oldDatabase]['tables'][$oldTable]['columns'][$column['name']])) {
2483 12
                            continue;
2484
                        }
2485
2486 4
                        $columnAliases = $aliases[$oldDatabase]['tables'][$oldTable]['columns'];
2487 4
                        $field->key->columns[$key]['name'] = $columnAliases[$column['name']];
2488 4
                        $flag = true;
2489
                    }
2490
                }
2491
2492
                // References.
2493 12
                if ($field->references === null) {
2494 12
                    continue;
2495
                }
2496
2497 8
                $refTable = $field->references->table->table;
2498
                // Replacing table.
2499 8
                if (! empty($aliases[$oldDatabase]['tables'][$refTable]['alias'])) {
2500
                    $field->references->table->table = $aliases[$oldDatabase]['tables'][$refTable]['alias'];
2501
                    $field->references->table->expr = '';
2502
                    $flag = true;
2503
                }
2504
2505
                // Replacing column names.
2506 8
                foreach ($field->references->columns as $key => $column) {
2507 8
                    if (empty($aliases[$oldDatabase]['tables'][$refTable]['columns'][$column])) {
2508 8
                        continue;
2509
                    }
2510
2511
                    $field->references->columns[$key] = $aliases[$oldDatabase]['tables'][$refTable]['columns'][$column];
2512
                    $flag = true;
2513
                }
2514
            }
2515 12
        } elseif ($statement->options->has('TRIGGER')) {
2516
            // Extracting the name of the old database and table from the
2517
            // statement to make sure the parameters are correct.
2518 8
            if (! empty($statement->table->database)) {
2519
                $oldDatabase = $statement->table->database;
2520
            }
2521
2522
            /**
2523
             * Old table name.
2524
             */
2525 8
            $oldTable = $statement->table->table;
2526
2527 8
            if (! empty($aliases[$oldDatabase]['tables'][$oldTable]['alias'])) {
2528 4
                $statement->table->table = $aliases[$oldDatabase]['tables'][$oldTable]['alias'];
2529 4
                $statement->table->expr = ''; // Force rebuild.
2530 4
                $flag = true;
2531
            }
2532
        }
2533
2534
        if (
2535 16
            $statement->options->has('TRIGGER')
2536 16
            || $statement->options->has('PROCEDURE')
2537 16
            || $statement->options->has('FUNCTION')
2538 16
            || $statement->options->has('VIEW')
2539
        ) {
2540
            // Replacing the body.
2541 12
            foreach ($statement->body as $token) {
2542
                // Replacing only symbols (that are not variables) and unknown identifiers.
2543 12
                $isSymbol = $token->type === TokenType::Symbol;
2544 12
                $isKeyword = $token->type === TokenType::Keyword;
2545 12
                $isNone = $token->type === TokenType::None;
2546 12
                $replaceToken = $isSymbol && ($token->flags & Token::FLAG_SYMBOL_VARIABLE) === 0
2547 12
                    || $isKeyword && ($token->flags & Token::FLAG_KEYWORD_RESERVED) === 0
2548 12
                    || $isNone;
2549 12
                if (! $replaceToken) {
2550 12
                    continue;
2551
                }
2552
2553 12
                $alias = $this->getAlias($aliases, $token->value);
2554 12
                if ($alias === '') {
2555 12
                    continue;
2556
                }
2557
2558
                // Replacing the token.
2559 4
                $token->token = Context::escape($alias);
2560 4
                $flag = true;
2561
            }
2562
        }
2563
2564 16
        return $statement->build();
2565
    }
2566
2567
    /**
2568
     * Generate comment
2569
     *
2570
     * @param string|null $sqlStatement SQL statement
2571
     * @param string      $comment1     Comment for dumped table
2572
     * @param string      $comment2     Comment for current table
2573
     * @param string      $tableAlias   Table alias
2574
     * @param string      $compat       Compatibility mode
2575
     */
2576 8
    protected function generateComment(
2577
        string|null $sqlStatement,
2578
        string $comment1,
2579
        string $comment2,
2580
        string $tableAlias,
2581
        string $compat,
2582
    ): string {
2583 8
        if ($sqlStatement === null) {
2584 8
            if (self::$noConstraintsComments) {
2585
                $sqlStatement = '';
2586
            } else {
2587 8
                $sqlStatement = "\n"
2588 8
                    . $this->exportComment()
2589 8
                    . $this->exportComment($comment1)
2590 8
                    . $this->exportComment();
2591
            }
2592
        }
2593
2594
        // comments for current table
2595 8
        if (! self::$noConstraintsComments) {
2596 8
            $sqlStatement .= "\n"
2597 8
                . $this->exportComment()
2598 8
                . $this->exportComment(
2599 8
                    $comment2 . ' ' . Util::backquoteCompat(
2600 8
                        $tableAlias,
2601 8
                        $compat,
2602 8
                        $this->useSqlBackquotes,
2603 8
                    ),
2604 8
                )
2605 8
                . $this->exportComment();
2606
        }
2607
2608 8
        return $sqlStatement;
2609
    }
2610
2611 12
    private function getTableStatus(string $db, string $table): string
2612
    {
2613 12
        $newCrlf = "\n";
2614 12
        $schemaCreate = '';
2615
2616 12
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2616
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
2617 12
        $result = $dbi->tryQuery(
2618 12
            'SHOW TABLE STATUS FROM ' . Util::backquote($db)
2619 12
            . ' WHERE Name = ' . $dbi->quoteString($table),
2620 12
        );
2621 12
        if ($result !== false && $result->numRows() > 0) {
2622 8
            $tmpres = $result->fetchAssoc();
2623
2624 8
            if ($this->doDates && ! empty($tmpres['Create_time'])) {
2625 4
                $schemaCreate .= $this->exportComment(
2626 4
                    __('Creation:') . ' '
2627 4
                    . Util::localisedDate(new DateTimeImmutable($tmpres['Create_time'])),
2628 4
                );
2629 4
                $newCrlf = $this->exportComment() . "\n";
2630
            }
2631
2632 8
            if ($this->doDates && ! empty($tmpres['Update_time'])) {
2633 4
                $schemaCreate .= $this->exportComment(
2634 4
                    __('Last update:') . ' '
2635 4
                    . Util::localisedDate(new DateTimeImmutable($tmpres['Update_time'])),
2636 4
                );
2637 4
                $newCrlf = $this->exportComment() . "\n";
2638
            }
2639
2640 8
            if ($this->doDates && ! empty($tmpres['Check_time'])) {
2641 4
                $schemaCreate .= $this->exportComment(
2642 4
                    __('Last check:') . ' '
2643 4
                    . Util::localisedDate(new DateTimeImmutable($tmpres['Check_time'])),
2644 4
                );
2645 4
                $newCrlf = $this->exportComment() . "\n";
2646
            }
2647
        }
2648
2649 12
        return $schemaCreate . $newCrlf;
2650
    }
2651
2652
    /** @param string[] $compats */
2653 4
    private function addCompatOptions(array $compats, OptionsPropertyMainGroup $generalOptions): void
2654
    {
2655 4
        $values = [];
2656 4
        foreach ($compats as $val) {
2657 4
            $values[$val] = $val;
2658
        }
2659
2660 4
        $leaf = new SelectPropertyItem(
2661 4
            'compatibility',
2662 4
            __(
2663 4
                'Database system or older MySQL server to maximize output compatibility with:',
2664 4
            ),
2665 4
        );
2666 4
        $leaf->setValues($values);
2667 4
        $leaf->setDoc(
2668 4
            ['manual_MySQL_Database_Administration', 'Server_SQL_mode'],
2669 4
        );
2670 4
        $generalOptions->addProperty($leaf);
2671
    }
2672
2673
    /** @inheritDoc */
2674 68
    public function setExportOptions(ServerRequest $request, array $exportConfig): void
2675
    {
2676 68
        $this->structureOrData = $this->setStructureOrData(
2677 68
            $request->getParsedBodyParam('sql_structure_or_data'),
2678 68
            $exportConfig['sql_structure_or_data'] ?? null,
2679 68
            StructureOrData::StructureAndData,
2680 68
        );
2681 68
        $this->useSqlBackquotes = $request->hasBodyParam('sql_backquotes');
2682 68
        $this->doRelation = (bool) ($request->getParsedBodyParam('sql_relation')
2683 68
            ?? $exportConfig['sql_relation'] ?? false);
2684 68
        $this->doMime = (bool) ($request->getParsedBodyParam('sql_mime') ?? $exportConfig['sql_mime'] ?? false);
2685 68
        $this->doDates = (bool) ($request->getParsedBodyParam('sql_dates') ?? $exportConfig['sql_dates'] ?? false);
2686 68
        $this->doComments = (bool) ($request->getParsedBodyParam('sql_include_comments')
2687 68
            ?? $exportConfig['sql_include_comments'] ?? false);
2688 68
        $this->headerComment = $this->setStringValue(
2689 68
            $request->getParsedBodyParam('sql_header_comment'),
2690 68
            $exportConfig['sql_header_comment'] ?? null,
2691 68
        );
2692 68
        $this->useTransaction = (bool) ($request->getParsedBodyParam('sql_use_transaction')
2693 68
            ?? $exportConfig['sql_use_transaction'] ?? false);
2694 68
        $this->disableForeignKey = (bool) ($request->getParsedBodyParam('sql_disable_fk')
2695 68
            ?? $exportConfig['sql_disable_fk'] ?? false);
2696 68
        $this->compatibility = $this->setCompatibility($this->setStringValue(
2697 68
            $request->getParsedBodyParam('sql_compatibility'),
2698 68
            $exportConfig['sql_compatibility'] ?? null,
2699 68
        ));
2700 68
        $this->createDatabase = (bool) ($request->getParsedBodyParam('sql_create_database')
2701 68
            ?? $exportConfig['sql_create_database'] ?? false);
2702 68
        $this->dropTable = (bool) ($request->getParsedBodyParam('sql_drop_table')
2703 68
            ?? $exportConfig['sql_drop_table'] ?? false);
2704 68
        $this->procedureFunction = (bool) ($request->getParsedBodyParam('sql_procedure_function')
2705 68
            ?? $exportConfig['sql_procedure_function'] ?? false);
2706 68
        $this->createTable = (bool) ($request->getParsedBodyParam('sql_create_table')
2707 68
            ?? $exportConfig['sql_create_table'] ?? false);
2708 68
        $this->type = $this->setType($this->setStringValue(
2709 68
            $request->getParsedBodyParam('sql_type'),
2710 68
            $exportConfig['sql_type'] ?? null,
2711 68
        ));
2712 68
        $this->createView = (bool) ($request->getParsedBodyParam('sql_create_view')
2713 68
            ?? $exportConfig['sql_create_view'] ?? false);
2714 68
        $this->createTrigger = (bool) ($request->getParsedBodyParam('sql_create_trigger')
2715 68
            ?? $exportConfig['sql_create_trigger'] ?? false);
2716 68
        $this->viewCurrentUser = (bool) ($request->getParsedBodyParam('sql_view_current_user')
2717 68
            ?? $exportConfig['sql_view_current_user'] ?? false);
2718 68
        $this->simpleViewExport = (bool) ($request->getParsedBodyParam('sql_simple_view_export')
2719 68
            ?? $exportConfig['sql_simple_view_export'] ?? false);
2720 68
        $this->ifNotExists = (bool) ($request->getParsedBodyParam('sql_if_not_exists')
2721 68
            ?? $exportConfig['sql_if_not_exists'] ?? false);
2722 68
        $this->orReplaceView = (bool) ($request->getParsedBodyParam('sql_or_replace_view')
2723 68
            ?? $exportConfig['sql_or_replace_view'] ?? false);
2724 68
        $this->autoIncrement = (bool) ($request->getParsedBodyParam('sql_auto_increment')
2725 68
            ?? $exportConfig['sql_auto_increment'] ?? false);
2726 68
        $this->truncate = (bool) ($request->getParsedBodyParam('sql_truncate')
2727 68
            ?? $exportConfig['sql_truncate'] ?? false);
2728 68
        $this->delayed = (bool) ($request->getParsedBodyParam('sql_delayed')
2729 68
            ?? $exportConfig['sql_delayed'] ?? false);
2730 68
        $this->ignore = (bool) ($request->getParsedBodyParam('sql_ignore')
2731 68
            ?? $exportConfig['sql_ignore'] ?? false);
2732 68
        $this->insertSyntax = $this->setInsertSyntax($this->setStringValue(
2733 68
            $request->getParsedBodyParam('sql_insert_syntax'),
2734 68
            $exportConfig['sql_insert_syntax'] ?? null,
2735 68
        ));
2736 68
        $this->maxQuerySize = $this->setMaxQuerySize(
2737 68
            $request->getParsedBodyParam('sql_max_query_size'),
2738 68
            $exportConfig['sql_max_query_size'] ?? null,
2739 68
        );
2740 68
        $this->hexForBinary = (bool) ($request->getParsedBodyParam('sql_hex_for_binary')
2741 68
            ?? $exportConfig['sql_hex_for_binary'] ?? false);
2742 68
        $this->utcTime = (bool) ($request->getParsedBodyParam('sql_utc_time')
2743 68
            ?? $exportConfig['sql_utc_time'] ?? false);
2744 68
        $this->dropDatabase = (bool) ($request->getParsedBodyParam('sql_drop_database')
2745 68
            ?? $exportConfig['sql_drop_database'] ?? false);
2746 68
        $this->viewsAsTables = (bool) ($request->getParsedBodyParam('sql_views_as_tables')
2747 68
            ?? $exportConfig['sql_views_as_tables'] ?? false);
2748 68
        $this->metadata = (bool) ($request->getParsedBodyParam('sql_metadata')
2749 68
            ?? $exportConfig['sql_metadata'] ?? false);
2750
    }
2751
2752 68
    private function setStringValue(mixed $fromRequest, mixed $fromConfig): string
2753
    {
2754 68
        if (is_string($fromRequest) && $fromRequest !== '') {
2755 28
            return $fromRequest;
2756
        }
2757
2758 68
        if (is_string($fromConfig) && $fromConfig !== '') {
2759
            return $fromConfig;
2760
        }
2761
2762 68
        return '';
2763
    }
2764
2765
    /** @return 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'NONE'|'ANSI'|'DB2'|'MAX...|'ORACLE'|'TRADITIONAL' at position 0 could not be parsed: Unknown type name ''NONE'' at position 0 in 'NONE'|'ANSI'|'DB2'|'MAXDB'|'MYSQL323'|'MYSQL40'|'MSSQL'|'ORACLE'|'TRADITIONAL'.
Loading history...
2766 68
    private function setCompatibility(string $mode): string
2767
    {
2768 68
        if (in_array($mode, ['ANSI', 'DB2', 'MAXDB', 'MYSQL323', 'MYSQL40', 'MSSQL', 'ORACLE', 'TRADITIONAL'], true)) {
2769 24
            return $mode;
2770
        }
2771
2772 52
        return 'NONE';
2773
    }
2774
2775
    public function hasCreateProcedureFunction(): bool
2776
    {
2777
        return $this->procedureFunction;
2778
    }
2779
2780
    public function hasCreateTable(): bool
2781
    {
2782
        return $this->createTable;
2783
    }
2784
2785
    /** @return 'INSERT'|'UPDATE'|'REPLACE' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'INSERT'|'UPDATE'|'REPLACE' at position 0 could not be parsed: Unknown type name ''INSERT'' at position 0 in 'INSERT'|'UPDATE'|'REPLACE'.
Loading history...
2786 68
    private function setType(string $type): string
2787
    {
2788 68
        if (in_array($type, ['UPDATE', 'REPLACE'], true)) {
2789 4
            return $type;
2790
        }
2791
2792 64
        return 'INSERT';
2793
    }
2794
2795
    public function hasCreateView(): bool
2796
    {
2797
        return $this->createView;
2798
    }
2799
2800
    public function hasCreateTrigger(): bool
2801
    {
2802
        return $this->createTrigger;
2803
    }
2804
2805
    public function setAutoIncrement(bool $autoIncrement): void
2806
    {
2807
        $this->autoIncrement = $autoIncrement;
2808
    }
2809
2810
    /** @return 'complete'|'extended'|'both'|'none' */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'complete'|'extended'|'both'|'none' at position 0 could not be parsed: Unknown type name ''complete'' at position 0 in 'complete'|'extended'|'both'|'none'.
Loading history...
2811 68
    private function setInsertSyntax(string $syntax): string
2812
    {
2813 68
        if (in_array($syntax, ['complete', 'extended', 'none'], true)) {
2814
            return $syntax;
2815
        }
2816
2817 68
        return 'both';
2818
    }
2819
2820
    /** @return int<0, max> */
2821 68
    private function setMaxQuerySize(mixed $fromRequest, mixed $fromConfig): int
2822
    {
2823 68
        if (is_numeric($fromRequest) && $fromRequest >= 0) {
2824
            return (int) $fromRequest;
2825
        }
2826
2827 68
        if (is_numeric($fromConfig) && $fromConfig >= 0) {
2828
            return (int) $fromConfig;
2829
        }
2830
2831 68
        return 50000;
2832
    }
2833
2834
    public function hasMetadata(): bool
2835
    {
2836
        return $this->metadata;
2837
    }
2838
}
2839