ExportSqlTest   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 1467
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 29
eloc 991
c 3
b 0
f 0
dl 0
loc 1467
rs 9.609

27 Methods

Rating   Name   Duplication   Size   Complexity  
B testGetTableDef() 0 75 2
A testGetTableDefWithError() 0 34 2
A tearDown() 0 6 1
B testGetTableDefForView() 0 75 1
A testGetTableComments() 0 42 1
A testExportComment() 0 28 1
A testExportFooter() 0 24 1
B testSetProperties() 0 190 1
A testExportRoutines() 0 13 1
A testExportHeader() 0 58 1
A testExportDBCreate() 0 71 1
A testExportEvents() 0 40 1
A testPossibleCRLF() 0 28 1
A testSetPropertiesWithHideSql() 0 12 1
A testGetTableDefStandIn() 0 23 1
A setUp() 0 24 1
A testExportDBHeader() 0 31 1
A testExportDBFooter() 0 19 1
A testExportDataWithIsView() 0 43 1
A testGetAlias() 0 30 1
B testExportData() 0 108 1
B testReplaceWithAlias() 0 114 1
B testExportDataWithUpdate() 0 84 1
A testExportDataWithError() 0 38 1
B testExportStructure() 0 111 1
A testMakeCreateTableMSSQLCompatible() 0 43 1
A testInitAlias() 0 28 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\Tests\Plugins\Export;
6
7
use PhpMyAdmin\ColumnFull;
8
use PhpMyAdmin\Config;
9
use PhpMyAdmin\ConfigStorage\Relation;
10
use PhpMyAdmin\ConfigStorage\RelationParameters;
11
use PhpMyAdmin\Current;
12
use PhpMyAdmin\DatabaseInterface;
13
use PhpMyAdmin\Dbal\ConnectionType;
14
use PhpMyAdmin\Export\Export;
15
use PhpMyAdmin\Plugins\Export\ExportSql;
16
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
17
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
18
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
19
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
20
use PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem;
21
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
22
use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
23
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
24
use PhpMyAdmin\Properties\Options\OptionsPropertyGroup;
25
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
26
use PhpMyAdmin\Table\Table;
27
use PhpMyAdmin\Tests\AbstractTestCase;
28
use PhpMyAdmin\Tests\FieldHelper;
29
use PhpMyAdmin\Tests\Stubs\DummyResult;
30
use PhpMyAdmin\Transformations;
31
use PHPUnit\Framework\Attributes\CoversClass;
32
use PHPUnit\Framework\Attributes\Medium;
33
use ReflectionMethod;
34
use ReflectionProperty;
35
36
use function ob_get_clean;
37
use function ob_start;
38
39
use const MYSQLI_NUM_FLAG;
40
use const MYSQLI_PRI_KEY_FLAG;
41
use const MYSQLI_TYPE_BLOB;
42
use const MYSQLI_TYPE_FLOAT;
43
use const MYSQLI_TYPE_LONG;
44
use const MYSQLI_TYPE_STRING;
45
use const MYSQLI_UNIQUE_KEY_FLAG;
46
47
#[CoversClass(ExportSql::class)]
48
#[Medium]
49
class ExportSqlTest extends AbstractTestCase
50
{
51
    protected ExportSql $object;
52
53
    /**
54
     * Configures global environment.
55
     */
56
    protected function setUp(): void
57
    {
58
        parent::setUp();
59
60
        $dbi = $this->createDatabaseInterface();
61
        DatabaseInterface::$instance = $dbi;
62
        Current::$database = '';
63
        Current::$table = '';
64
        $GLOBALS['lang'] = 'en';
65
        Config::getInstance()->selectedServer['DisableIS'] = true;
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

65
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = true;

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...
66
        $GLOBALS['output_kanji_conversion'] = false;
67
        $GLOBALS['buffer_needed'] = false;
68
        $GLOBALS['asfile'] = false;
69
        $GLOBALS['save_on_server'] = false;
70
        $GLOBALS['plugin_param'] = [];
71
        $GLOBALS['plugin_param']['export_type'] = 'table';
72
        $GLOBALS['plugin_param']['single_table'] = false;
73
74
        $this->object = new ExportSql(
75
            new Relation($dbi),
76
            new Export($dbi),
77
            new Transformations(),
78
        );
79
        $this->object->useSqlBackquotes(false);
80
    }
81
82
    /**
83
     * tearDown for test cases
84
     */
85
    protected function tearDown(): void
86
    {
87
        parent::tearDown();
88
89
        DatabaseInterface::$instance = null;
90
        unset($this->object);
91
    }
92
93
    public function testSetPropertiesWithHideSql(): void
94
    {
95
        // test with hide structure and hide sql as true
96
        $GLOBALS['plugin_param']['export_type'] = 'table';
97
        $GLOBALS['plugin_param']['single_table'] = false;
98
99
        $method = new ReflectionMethod(ExportSql::class, 'setProperties');
100
        $properties = $method->invoke($this->object, null);
101
102
        self::assertInstanceOf(ExportPluginProperties::class, $properties);
103
        self::assertSame('SQL', $properties->getText());
104
        self::assertNull($properties->getOptions());
105
    }
106
107
    public function testSetProperties(): void
108
    {
109
        // test with hide structure and hide sql as false
110
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
111
            ->disableOriginalConstructor()
112
            ->getMock();
113
114
        $dbi->expects(self::once())
115
            ->method('getCompatibilities')
116
            ->willReturn(['v1', 'v2']);
117
118
        DatabaseInterface::$instance = $dbi;
119
        $GLOBALS['plugin_param']['export_type'] = 'server';
120
        $GLOBALS['plugin_param']['single_table'] = false;
121
122
        $relationParameters = RelationParameters::fromArray([
123
            'db' => 'db',
124
            'relation' => 'relation',
125
            'column_info' => 'column_info',
126
            'relwork' => true,
127
            'mimework' => true,
128
        ]);
129
        (new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
130
131
        $method = new ReflectionMethod(ExportSql::class, 'setProperties');
132
        $properties = $method->invoke($this->object, null);
133
134
        self::assertInstanceOf(ExportPluginProperties::class, $properties);
135
        self::assertSame('SQL', $properties->getText());
136
137
        $options = $properties->getOptions();
138
139
        self::assertInstanceOf(OptionsPropertyRootGroup::class, $options);
140
141
        $generalOptionsArray = $options->getProperties();
142
143
        $generalOptions = $generalOptionsArray->current();
144
        $generalOptionsArray->next();
145
146
        self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
147
148
        $properties = $generalOptions->getProperties();
149
150
        $property = $properties->current();
151
        $properties->next();
152
153
        self::assertInstanceOf(OptionsPropertySubgroup::class, $property);
154
155
        self::assertInstanceOf(
156
            BoolPropertyItem::class,
157
            $property->getSubgroupHeader(),
158
        );
159
160
        $leaves = $property->getProperties();
161
162
        $leaf = $leaves->current();
163
        $leaves->next();
164
        self::assertInstanceOf(TextPropertyItem::class, $leaf);
165
166
        $leaf = $leaves->current();
167
        $leaves->next();
168
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
169
170
        $leaf = $leaves->current();
171
        $leaves->next();
172
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
173
174
        $leaf = $leaves->current();
175
        $leaves->next();
176
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
177
178
        $property = $properties->current();
179
        $properties->next();
180
        self::assertInstanceOf(BoolPropertyItem::class, $property);
181
182
        $property = $properties->current();
183
        $properties->next();
184
        self::assertInstanceOf(BoolPropertyItem::class, $property);
185
186
        $property = $properties->current();
187
        $properties->next();
188
        self::assertInstanceOf(BoolPropertyItem::class, $property);
189
190
        $property = $properties->current();
191
        $properties->next();
192
        self::assertInstanceOf(BoolPropertyItem::class, $property);
193
194
        $property = $properties->current();
195
        $properties->next();
196
        self::assertInstanceOf(SelectPropertyItem::class, $property);
197
198
        self::assertSame(
199
            ['v1' => 'v1', 'v2' => 'v2'],
200
            $property->getValues(),
201
        );
202
203
        $property = $properties->current();
204
        self::assertInstanceOf(OptionsPropertySubgroup::class, $property);
205
206
        self::assertInstanceOf(
207
            RadioPropertyItem::class,
208
            $property->getSubgroupHeader(),
209
        );
210
211
        $structureOptions = $generalOptionsArray->current();
212
        $generalOptionsArray->next();
213
214
        self::assertInstanceOf(OptionsPropertyMainGroup::class, $structureOptions);
215
216
        $properties = $structureOptions->getProperties();
217
218
        $property = $properties->current();
219
        $properties->next();
220
221
        self::assertInstanceOf(OptionsPropertySubgroup::class, $property);
222
223
        self::assertInstanceOf(
224
            MessageOnlyPropertyItem::class,
225
            $property->getSubgroupHeader(),
226
        );
227
228
        $leaves = $property->getProperties();
229
230
        $leaf = $leaves->current();
231
        $leaves->next();
232
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
233
234
        $leaf = $leaves->current();
235
        $leaves->next();
236
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
237
238
        self::assertSame(
239
            'Add <code>DROP TABLE / VIEW / PROCEDURE / FUNCTION / EVENT</code><code> / TRIGGER</code> statement',
240
            $leaf->getText(),
241
        );
242
243
        $leaf = $leaves->current();
244
        $leaves->next();
245
        self::assertInstanceOf(OptionsPropertySubgroup::class, $leaf);
246
247
        self::assertCount(
248
            2,
249
            $leaf->getProperties(),
250
        );
251
252
        self::assertInstanceOf(
253
            BoolPropertyItem::class,
254
            $leaf->getSubgroupHeader(),
255
        );
256
257
        $leaf = $leaves->current();
258
        $leaves->next();
259
        self::assertInstanceOf(OptionsPropertySubgroup::class, $leaf);
260
261
        self::assertCount(
262
            3,
263
            $leaf->getProperties(),
264
        );
265
266
        self::assertInstanceOf(
267
            BoolPropertyItem::class,
268
            $leaf->getSubgroupHeader(),
269
        );
270
271
        $leaf = $leaves->current();
272
        $leaves->next();
273
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
274
275
        $leaf = $leaves->current();
276
        $leaves->next();
277
        self::assertInstanceOf(BoolPropertyItem::class, $leaf);
278
279
        $property = $properties->current();
280
        self::assertInstanceOf(BoolPropertyItem::class, $property);
281
282
        $dataOptions = $generalOptionsArray->current();
283
        self::assertInstanceOf(OptionsPropertyMainGroup::class, $dataOptions);
284
285
        $properties = $dataOptions->getProperties();
286
287
        self::assertCount(7, $properties);
288
289
        $properties->next();
0 ignored issues
show
Bug introduced by
The method next() does not exist on Countable. It seems like you code against a sub-type of Countable such as PHPUnit\TextUI\Configura...strapCollectionIterator or PHPUnit\TextUI\Configura...iableCollectionIterator or SplDoublyLinkedList or PHPUnit\TextUI\Configura...tFileCollectionIterator or HttpMessage or HttpRequestPool or PHPUnit\TextUI\Configura...ctoryCollectionIterator or RdKafka\Metadata\Collection or Phar or PHPUnit\TextUI\Configura...ttingCollectionIterator or PHPUnit\TextUI\Configura...SuiteCollectionIterator or Yaf_Config_Simple or PHP_CodeSniffer\Files\FileList or SplFixedArray or SplObjectStorage or Yaf\Session or SQLiteResult or PHPUnit\TextUI\Configura...GroupCollectionIterator or PHPUnit\TextUI\Configura...ctoryCollectionIterator or Cassandra\UserTypeValue or GlobIterator or Imagick or Cassandra\Collection or SimpleXMLElement or TheSeer\Tokenizer\TokenCollection or http\Message or PHPUnit\TextUI\Configura...\FileCollectionIterator or Yaf_Session or SplPriorityQueue or Cassandra\Map or Yaf\Config\Simple or CachingIterator or Yaf\Config\Ini or PHPUnit\TextUI\Configura...stantCollectionIterator or FG\ASN1\Construct or PHPUnit\TextUI\Configura...ctoryCollectionIterator or Yaf_Config_Ini or SplHeap or Cassandra\Set or SimpleXMLIterator or ArrayIterator or Cassandra\Tuple. ( Ignorable by Annotation )

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

289
        $properties->/** @scrutinizer ignore-call */ 
290
                     next();
Loading history...
290
291
        $property = $properties->current();
0 ignored issues
show
Bug introduced by
The method current() does not exist on Countable. It seems like you code against a sub-type of Countable such as PHPUnit\TextUI\Configura...strapCollectionIterator or PHPUnit\TextUI\Configura...iableCollectionIterator or SplDoublyLinkedList or PHPUnit\TextUI\Configura...tFileCollectionIterator or HttpMessage or HttpRequestPool or PHPUnit\TextUI\Configura...ctoryCollectionIterator or RdKafka\Metadata\Collection or Phar or PHPUnit\TextUI\Configura...ttingCollectionIterator or PHPUnit\TextUI\Configura...SuiteCollectionIterator or Yaf_Config_Simple or PHP_CodeSniffer\Files\FileList or SplFixedArray or SplObjectStorage or Yaf\Session or SQLiteResult or PHPUnit\TextUI\Configura...GroupCollectionIterator or PHPUnit\TextUI\Configura...ctoryCollectionIterator or Cassandra\UserTypeValue or GlobIterator or Imagick or Cassandra\Collection or SimpleXMLElement or TheSeer\Tokenizer\TokenCollection or http\Message or PHPUnit\TextUI\Configura...\FileCollectionIterator or Yaf_Session or SplPriorityQueue or Cassandra\Map or Yaf\Config\Simple or CachingIterator or Yaf\Config\Ini or PHPUnit\TextUI\Configura...stantCollectionIterator or FG\ASN1\Construct or PHPUnit\TextUI\Configura...ctoryCollectionIterator or Yaf_Config_Ini or SplHeap or Cassandra\Set or SimpleXMLIterator or ArrayIterator or Cassandra\Tuple. ( Ignorable by Annotation )

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

291
        /** @scrutinizer ignore-call */ 
292
        $property = $properties->current();
Loading history...
292
        self::assertInstanceOf(OptionsPropertyGroup::class, $property);
293
294
        self::assertCount(
295
            2,
296
            $property->getProperties(),
297
        );
298
    }
299
300
    public function testExportRoutines(): void
301
    {
302
        $GLOBALS['sql_drop_table'] = true;
303
304
        $this->expectOutputString(
305
            "\n" . 'DELIMITER $$' . "\n" . 'DROP PROCEDURE IF EXISTS `test_proc1`$$' . "\n" . 'CREATE PROCEDURE'
306
                . ' `test_proc1` (`p` INT)   BEGIN END$$' . "\n\n" . 'DROP PROCEDURE IF EXISTS'
307
                . ' `test_proc2`$$' . "\n" . 'CREATE PROCEDURE `test_proc2` (`p` INT)   BEGIN END$$' . "\n\n" . 'DROP'
308
                . ' FUNCTION IF EXISTS `test_func`$$' . "\n" . 'CREATE FUNCTION'
309
                . ' `test_func` (`p` INT) RETURNS INT(11)  BEGIN END$$' . "\n\n" . 'DELIMITER ;' . "\n",
310
        );
311
312
        $this->object->exportRoutines('test_db');
313
    }
314
315
    public function testExportComment(): void
316
    {
317
        $method = new ReflectionMethod(ExportSql::class, 'exportComment');
318
319
        $GLOBALS['sql_include_comments'] = true;
320
321
        self::assertSame(
322
            '--' . "\n",
323
            $method->invoke($this->object, ''),
324
        );
325
326
        self::assertSame(
327
            '-- Comment' . "\n",
328
            $method->invoke($this->object, 'Comment'),
329
        );
330
331
        $GLOBALS['sql_include_comments'] = false;
332
333
        self::assertSame(
334
            '',
335
            $method->invoke($this->object, 'Comment'),
336
        );
337
338
        unset($GLOBALS['sql_include_comments']);
339
340
        self::assertSame(
341
            '',
342
            $method->invoke($this->object, 'Comment'),
343
        );
344
    }
345
346
    public function testPossibleCRLF(): void
347
    {
348
        $method = new ReflectionMethod(ExportSql::class, 'possibleCRLF');
349
350
        $GLOBALS['sql_include_comments'] = true;
351
352
        self::assertSame(
353
            "\n",
354
            $method->invoke($this->object, ''),
355
        );
356
357
        self::assertSame(
358
            "\n",
359
            $method->invoke($this->object, 'Comment'),
360
        );
361
362
        $GLOBALS['sql_include_comments'] = false;
363
364
        self::assertSame(
365
            '',
366
            $method->invoke($this->object, 'Comment'),
367
        );
368
369
        unset($GLOBALS['sql_include_comments']);
370
371
        self::assertSame(
372
            '',
373
            $method->invoke($this->object, 'Comment'),
374
        );
375
    }
376
377
    public function testExportFooter(): void
378
    {
379
        $GLOBALS['sql_disable_fk'] = true;
380
        $GLOBALS['sql_use_transaction'] = true;
381
        $GLOBALS['charset'] = 'utf-8';
382
        $GLOBALS['sql_utc_time'] = true;
383
        $GLOBALS['old_tz'] = 'GMT';
384
        $GLOBALS['asfile'] = 'yes';
385
        $GLOBALS['output_charset_conversion'] = 'utf-8';
386
387
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
388
            ->disableOriginalConstructor()
389
            ->getMock();
390
391
        $dbi->expects(self::once())
392
            ->method('query')
393
            ->with('SET time_zone = "GMT"');
394
395
        DatabaseInterface::$instance = $dbi;
396
397
        $this->expectOutputString('SET FOREIGN_KEY_CHECKS=1;' . "\n" . 'COMMIT;' . "\n");
398
399
        self::assertTrue(
400
            $this->object->exportFooter(),
401
        );
402
    }
403
404
    public function testExportHeader(): void
405
    {
406
        $GLOBALS['sql_compatibility'] = 'NONE';
407
        $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

407
        $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...
408
        $config->selectedServer['host'] = 'localhost';
409
        $config->selectedServer['port'] = 80;
410
        $GLOBALS['sql_disable_fk'] = true;
411
        $GLOBALS['sql_use_transaction'] = true;
412
        $GLOBALS['sql_utc_time'] = true;
413
        $GLOBALS['old_tz'] = 'GMT';
414
        $GLOBALS['asfile'] = 'yes';
415
        $GLOBALS['output_charset_conversion'] = 'utf-8';
416
        $GLOBALS['sql_header_comment'] = "h1C\nh2C";
417
        $GLOBALS['sql_use_transaction'] = true;
418
        $GLOBALS['sql_include_comments'] = true;
419
        $GLOBALS['charset'] = 'utf-8';
420
421
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
422
            ->disableOriginalConstructor()
423
            ->getMock();
424
425
        $dbi->expects(self::once())
426
            ->method('tryQuery')
427
            ->with('SET SQL_MODE=""');
428
429
        $dbi->expects(self::once())
430
            ->method('fetchValue')
431
            ->with('SELECT @@session.time_zone')
432
            ->willReturn('old_tz');
433
434
        $dbi->expects(self::once())
435
            ->method('query')
436
            ->with('SET time_zone = "+00:00"');
437
438
        DatabaseInterface::$instance = $dbi;
439
440
        ob_start();
441
        self::assertTrue(
442
            $this->object->exportHeader(),
443
        );
444
        $result = ob_get_clean();
445
446
        self::assertIsString($result);
447
448
        self::assertStringContainsString('h1C', $result);
449
450
        self::assertStringContainsString('h2C', $result);
451
452
        self::assertStringContainsString("SET FOREIGN_KEY_CHECKS=0;\n", $result);
453
454
        self::assertStringContainsString('40101 SET', $result);
455
456
        self::assertStringContainsString(
457
            "SET FOREIGN_KEY_CHECKS=0;\n" .
458
            "SET SQL_MODE = \"NO_AUTO_VALUE_ON_ZERO\";\n" .
459
            "START TRANSACTION;\n" .
460
            "SET time_zone = \"+00:00\";\n",
461
            $result,
462
        );
463
    }
464
465
    public function testExportDBCreate(): void
466
    {
467
        $GLOBALS['sql_compatibility'] = 'NONE';
468
        $GLOBALS['sql_drop_database'] = true;
469
        $GLOBALS['sql_create_database'] = true;
470
        $GLOBALS['sql_create_table'] = true;
471
        $GLOBALS['sql_create_view'] = true;
472
473
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
474
            ->disableOriginalConstructor()
475
            ->getMock();
476
477
        $dbi->expects(self::once())
478
            ->method('getDbCollation')
479
            ->with('db')
480
            ->willReturn('utf8_general_ci');
481
482
        DatabaseInterface::$instance = $dbi;
483
484
        $this->object->useSqlBackquotes(true);
485
486
        ob_start();
487
        self::assertTrue(
488
            $this->object->exportDBCreate('db', 'database'),
489
        );
490
        $result = ob_get_clean();
491
492
        self::assertIsString($result);
493
494
        self::assertStringContainsString("DROP DATABASE IF EXISTS `db`;\n", $result);
495
496
        self::assertStringContainsString(
497
            'CREATE DATABASE IF NOT EXISTS `db` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;',
498
            $result,
499
        );
500
501
        self::assertStringContainsString('USE `db`;', $result);
502
503
        // case2: no backquotes
504
        unset($GLOBALS['sql_compatibility']);
505
        Config::getInstance()->selectedServer['DisableIS'] = true;
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

505
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = true;

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...
506
507
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
508
            ->disableOriginalConstructor()
509
            ->getMock();
510
511
        $dbi->expects(self::once())
512
            ->method('getDbCollation')
513
            ->with('db')
514
            ->willReturn('testcollation');
515
516
        DatabaseInterface::$instance = $dbi;
517
518
        $this->object->useSqlBackquotes(false);
519
520
        ob_start();
521
        self::assertTrue(
522
            $this->object->exportDBCreate('db', 'database'),
523
        );
524
        $result = ob_get_clean();
525
526
        self::assertIsString($result);
527
528
        self::assertStringContainsString("DROP DATABASE IF EXISTS db;\n", $result);
529
530
        self::assertStringContainsString(
531
            'CREATE DATABASE IF NOT EXISTS db DEFAULT CHARACTER SET testcollation;',
532
            $result,
533
        );
534
535
        self::assertStringContainsString('USE db;', $result);
536
    }
537
538
    public function testExportDBHeader(): void
539
    {
540
        $GLOBALS['sql_compatibility'] = 'MSSQL';
541
        $GLOBALS['sql_include_comments'] = true;
542
543
        $this->object->useSqlBackquotes(true);
544
545
        ob_start();
546
        self::assertTrue(
547
            $this->object->exportDBHeader('testDB'),
548
        );
549
        $result = ob_get_clean();
550
551
        self::assertIsString($result);
552
553
        self::assertStringContainsString('&quot;testDB&quot;', $result);
554
555
        // case 2
556
        unset($GLOBALS['sql_compatibility']);
557
558
        $this->object->useSqlBackquotes(false);
559
560
        ob_start();
561
        self::assertTrue(
562
            $this->object->exportDBHeader('testDB'),
563
        );
564
        $result = ob_get_clean();
565
566
        self::assertIsString($result);
567
568
        self::assertStringContainsString('testDB', $result);
569
    }
570
571
    public function testExportEvents(): void
572
    {
573
        $GLOBALS['sql_structure_or_data'] = 'structure';
574
        $GLOBALS['sql_procedure_function'] = true;
575
576
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
577
            ->disableOriginalConstructor()
578
            ->getMock();
579
580
        $dbi->expects(self::once())
581
            ->method('fetchResult')
582
            ->with('SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA= \'db\'')
583
            ->willReturn(['f1', 'f2']);
584
585
        $dbi->expects(self::exactly(2))
586
            ->method('fetchValue')
587
            ->willReturnMap([
588
                ['SHOW CREATE EVENT `db`.`f1`', 'Create Event', ConnectionType::User, 'f1event'],
589
                ['SHOW CREATE EVENT `db`.`f2`', 'Create Event', ConnectionType::User, 'f2event'],
590
            ]);
591
        $dbi->expects(self::any())->method('quoteString')
592
            ->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
593
594
        DatabaseInterface::$instance = $dbi;
595
596
        ob_start();
597
        self::assertTrue(
598
            $this->object->exportEvents('db'),
599
        );
600
        $result = ob_get_clean();
601
602
        self::assertIsString($result);
603
604
        self::assertStringContainsString("DELIMITER $$\n", $result);
605
606
        self::assertStringContainsString("DELIMITER ;\n", $result);
607
608
        self::assertStringContainsString("f1event$$\n", $result);
609
610
        self::assertStringContainsString("f2event$$\n", $result);
611
    }
612
613
    public function testExportDBFooter(): void
614
    {
615
        $this->object->sqlConstraints = 'SqlConstraints';
616
        $GLOBALS['sql_structure_or_data'] = 'structure';
617
        $GLOBALS['sql_procedure_function'] = true;
618
619
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
620
            ->disableOriginalConstructor()
621
            ->getMock();
622
623
        DatabaseInterface::$instance = $dbi;
624
625
        ob_start();
626
        self::assertTrue(
627
            $this->object->exportDBFooter('db'),
628
        );
629
        $result = ob_get_clean();
630
631
        self::assertSame('SqlConstraints', $result);
632
    }
633
634
    public function testGetTableDefStandIn(): void
635
    {
636
        $GLOBALS['sql_drop_table'] = true;
637
        $GLOBALS['sql_if_not_exists'] = true;
638
639
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
640
            ->disableOriginalConstructor()
641
            ->getMock();
642
643
        $dbi->expects(self::once())
644
            ->method('getColumnsFull')
645
            ->with('db', 'view')
646
            ->willReturn(['cname' => ['Type' => 'int']]);
647
648
        DatabaseInterface::$instance = $dbi;
649
650
        $result = $this->object->getTableDefStandIn('db', 'view');
651
652
        self::assertStringContainsString('DROP VIEW IF EXISTS `view`;', $result);
653
654
        self::assertStringContainsString(
655
            'CREATE TABLE IF NOT EXISTS `view` (' . "\n" . '`cname` int' . "\n" . ');' . "\n",
656
            $result,
657
        );
658
    }
659
660
    public function testGetTableDefForView(): void
661
    {
662
        $GLOBALS['sql_drop_table'] = true;
663
        $GLOBALS['sql_if_not_exists'] = true;
664
665
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
666
            ->disableOriginalConstructor()
667
            ->getMock();
668
        $dbi->expects(self::any())->method('quoteString')
669
            ->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
670
671
        $dbi->expects(self::any())
672
            ->method('getColumns')
673
            ->with('db', 'view')
674
            ->willReturn([
675
                new ColumnFull(
676
                    'fname',
677
                    'char',
678
                    'utf-8',
679
                    false,
680
                    '',
681
                    'a',
682
                    '',
683
                    '',
684
                    'cmt',
685
                ),
686
            ]);
687
688
        DatabaseInterface::$instance = $dbi;
689
        $GLOBALS['sql_compatibility'] = 'MSSQL';
690
691
        $method = new ReflectionMethod(ExportSql::class, 'getTableDefForView');
692
        $result = $method->invoke($this->object, 'db', 'view');
693
694
        self::assertSame(
695
            "CREATE TABLE `view`(\n" .
696
            "    `fname` char COLLATE utf-8 NOT NULL DEFAULT 'a' COMMENT 'cmt'\n" .
697
            ");\n",
698
            $result,
699
        );
700
701
        // case 2
702
        unset($GLOBALS['sql_compatibility']);
703
704
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
705
            ->disableOriginalConstructor()
706
            ->getMock();
707
        $dbi->expects(self::any())->method('quoteString')
708
            ->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
709
710
        $dbi->expects(self::any())
711
            ->method('getColumns')
712
            ->with('db', 'view')
713
            ->willReturn([
714
                new ColumnFull(
715
                    'fname',
716
                    'char',
717
                    'utf-8',
718
                    true,
719
                    '',
720
                    null,
721
                    '',
722
                    '',
723
                    'cmt',
724
                ),
725
            ]);
726
        DatabaseInterface::$instance = $dbi;
727
728
        $result = $method->invoke($this->object, 'db', 'view');
729
730
        self::assertSame(
731
            "CREATE TABLE IF NOT EXISTS `view`(\n" .
732
            "    `fname` char COLLATE utf-8 DEFAULT NULL COMMENT 'cmt'\n" .
733
            ");\n",
734
            $result,
735
        );
736
    }
737
738
    public function testGetTableDef(): void
739
    {
740
        $GLOBALS['sql_compatibility'] = 'MSSQL';
741
        $GLOBALS['sql_auto_increment'] = true;
742
        $GLOBALS['sql_drop_table'] = true;
743
        $GLOBALS['sql_if_not_exists'] = true;
744
        $GLOBALS['sql_include_comments'] = true;
745
        $this->object->sqlConstraints = null;
746
747
        if (isset($GLOBALS['no_constraints_comments'])) {
748
            unset($GLOBALS['no_constraints_comments']);
749
        }
750
751
        $createTableStatement = <<<'SQL'
752
CREATE TABLE `table` (
753
    `payment_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
754
    `customer_id` smallint(5) unsigned NOT NULL,
755
    `staff_id` tinyint(3) unsigned NOT NULL,
756
    `rental_id` int(11) DEFAULT NULL,
757
    `amount` decimal(5,2) NOT NULL,
758
    `payment_date` datetime NOT NULL,
759
    `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
760
    PRIMARY KEY (`payment_id`),
761
    KEY `idx_fk_staff_id` (`staff_id`),
762
    KEY `idx_fk_customer_id` (`customer_id`),
763
    KEY `fk_payment_rental` (`rental_id`),
764
    CONSTRAINT `fk_payment_customer`
765
        FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,
766
    CONSTRAINT `fk_payment_rental`
767
        FOREIGN KEY (`rental_id`) REFERENCES `rental` (`rental_id`) ON DELETE SET NULL ON UPDATE CASCADE,
768
    CONSTRAINT `fk_payment_staff`
769
        FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
770
) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
771
SQL;
772
        $isViewQuery = 'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'db\' AND TABLE_NAME = \'table\'';
773
774
        $dbiDummy = $this->createDbiDummy();
775
        // phpcs:disable Generic.Files.LineLength.TooLong
776
        $dbiDummy->addResult(
777
            'SHOW TABLE STATUS FROM `db` WHERE Name = \'table\'',
778
            [['table', 'InnoDB', '10', 'Dynamic', '3', '5461', '16384', '0', '0', '0', '1', '2000-01-01 10:00:00', '2000-01-02 12:00:00', '2000-01-02 13:00:00', 'utf8mb4_general_ci', null, '', '', '0', 'N']],
779
            ['Name', 'Engine', 'Version', 'Row_format', 'Rows', 'Avg_row_length', 'Data_length', 'Max_data_length', 'Index_length', 'Data_free', 'Auto_increment', 'Create_time', 'Update_time', 'Check_time', 'Collation', 'Checksum', 'Create_options', 'Comment', 'Max_index_length', 'Temporary'],
780
        );
781
        // phpcs:enable
782
        $dbiDummy->addResult($isViewQuery, []);
783
        $dbiDummy->addResult($isViewQuery, []);
784
        $dbiDummy->addResult('USE `db`', true);
785
        $dbiDummy->addResult(
786
            'SHOW CREATE TABLE `db`.`table`',
787
            [['table', $createTableStatement]],
788
            ['Table', 'Create Table'],
789
        );
790
791
        DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy);
792
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

792
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
793
794
        $this->object->useSqlBackquotes(true);
795
796
        $result = $this->object->getTableDef('db', 'table', true, true, false);
797
798
        $dbiDummy->assertAllQueriesConsumed();
799
        self::assertStringContainsString('-- Creation: Jan 01, 2000 at 10:00 AM', $result);
800
        self::assertStringContainsString('-- Last update: Jan 02, 2000 at 12:00 PM', $result);
801
        self::assertStringContainsString('-- Last check: Jan 02, 2000 at 01:00 PM', $result);
802
        self::assertStringContainsString('DROP TABLE IF EXISTS `table`;', $result);
803
        self::assertStringContainsString('CREATE TABLE `table`', $result);
804
        self::assertIsString($this->object->sqlConstraints);
805
        self::assertStringContainsString('-- Constraints for dumped tables', $this->object->sqlConstraints);
0 ignored issues
show
Bug introduced by
It seems like $this->object->sqlConstraints can also be of type null; however, parameter $haystack of PHPUnit\Framework\Assert...tStringContainsString() 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

805
        self::assertStringContainsString('-- Constraints for dumped tables', /** @scrutinizer ignore-type */ $this->object->sqlConstraints);
Loading history...
806
        self::assertStringContainsString('-- Constraints for table "table"', $this->object->sqlConstraints);
807
        self::assertStringContainsString('ALTER TABLE "table"', $this->object->sqlConstraints);
808
        self::assertStringContainsString('ADD CONSTRAINT', $this->object->sqlConstraints);
809
        self::assertStringContainsString('ALTER TABLE "table"', $this->object->sqlConstraintsQuery);
810
        self::assertStringContainsString('ADD CONSTRAINT', $this->object->sqlConstraintsQuery);
811
        self::assertStringContainsString('ALTER TABLE "table"', $GLOBALS['sql_drop_foreign_keys']);
812
        self::assertStringContainsString('DROP FOREIGN KEY', $GLOBALS['sql_drop_foreign_keys']);
813
    }
814
815
    public function testGetTableDefWithError(): void
816
    {
817
        $GLOBALS['sql_compatibility'] = '';
818
        $GLOBALS['sql_auto_increment'] = true;
819
        $GLOBALS['sql_drop_table'] = true;
820
        $GLOBALS['sql_if_not_exists'] = true;
821
        $GLOBALS['sql_include_comments'] = true;
822
823
        $this->object->sqlConstraints = null;
824
825
        if (isset($GLOBALS['no_constraints_comments'])) {
826
            unset($GLOBALS['no_constraints_comments']);
827
        }
828
829
        $isViewQuery = 'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'db\' AND TABLE_NAME = \'table\'';
830
831
        $dbiDummy = $this->createDbiDummy();
832
        $dbiDummy->addResult('SHOW TABLE STATUS FROM `db` WHERE Name = \'table\'', []);
833
        $dbiDummy->addResult($isViewQuery, []);
834
        $dbiDummy->addResult($isViewQuery, []);
835
        $dbiDummy->addResult('USE `db`', true);
836
        $dbiDummy->addResult('SHOW CREATE TABLE `db`.`table`', []);
837
        $dbiDummy->addErrorCode('error occurred');
838
839
        DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy);
840
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

840
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
841
842
        $this->object->useSqlBackquotes(false);
843
844
        $result = $this->object->getTableDef('db', 'table', true, true, false);
845
846
        $dbiDummy->assertAllQueriesConsumed();
847
        $dbiDummy->assertAllErrorCodesConsumed();
848
        self::assertStringContainsString('-- Error reading structure for table db.table: error occurred', $result);
849
    }
850
851
    public function testGetTableComments(): void
852
    {
853
        $relationParameters = RelationParameters::fromArray([
854
            'relwork' => true,
855
            'commwork' => true,
856
            'mimework' => true,
857
            'db' => 'database',
858
            'relation' => 'rel',
859
            'column_info' => 'col',
860
        ]);
861
        (new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
862
        $GLOBALS['sql_include_comments'] = true;
863
864
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
865
            ->disableOriginalConstructor()
866
            ->getMock();
867
868
        $dbi->expects(self::exactly(2))
869
            ->method('fetchResult')
870
            ->willReturn(
871
                ['foo' => ['foreign_table' => 'ftable', 'foreign_field' => 'ffield']],
872
                ['fieldname' => ['values' => 'test-', 'transformation' => 'testfoo', 'mimetype' => 'test<']],
873
            );
874
875
        DatabaseInterface::$instance = $dbi;
876
        $this->object->relation = new Relation($dbi);
877
878
        $method = new ReflectionMethod(ExportSql::class, 'getTableComments');
879
        $result = $method->invoke($this->object, 'db', '', true, true);
880
881
        self::assertStringContainsString(
882
            '-- MEDIA TYPES FOR TABLE :' . "\n"
883
            . '--   fieldname' . "\n"
884
            . '--       Test<',
885
            $result,
886
        );
887
888
        self::assertStringContainsString(
889
            '-- RELATIONSHIPS FOR TABLE :' . "\n"
890
            . '--   foo' . "\n"
891
            . '--       ftable -> ffield',
892
            $result,
893
        );
894
    }
895
896
    public function testExportStructure(): void
897
    {
898
        $GLOBALS['sql_compatibility'] = 'MSSQL';
899
        $GLOBALS['sql_include_comments'] = true;
900
901
        $this->object->useSqlBackquotes(true);
902
903
        // case 1
904
        ob_start();
905
        self::assertTrue(
906
            $this->object->exportStructure(
907
                'test_db',
908
                'test_table',
909
                'create_table',
910
                'test',
911
            ),
912
        );
913
        $result = ob_get_clean();
914
915
        self::assertIsString($result);
916
        self::assertStringContainsString('-- Table structure for table &quot;test_table&quot;', $result);
917
        self::assertStringContainsString('CREATE TABLE `test_table`', $result);
918
919
        // case 2
920
        unset($GLOBALS['sql_compatibility']);
921
922
        $GLOBALS['sql_create_trigger'] = true;
923
        $GLOBALS['sql_drop_table'] = true;
924
925
        $this->object->useSqlBackquotes(false);
926
927
        ob_start();
928
        self::assertTrue(
929
            $this->object->exportStructure(
930
                'test_db',
931
                'test_table',
932
                'triggers',
933
                'test',
934
            ),
935
        );
936
        $result = ob_get_clean();
937
938
        self::assertIsString($result);
939
        self::assertStringContainsString('-- Triggers test_table', $result);
940
        self::assertStringContainsString(
941
            "CREATE TRIGGER `test_trigger` AFTER INSERT ON `test_table` FOR EACH ROW BEGIN END\n$$",
942
            $result,
943
        );
944
945
        unset($GLOBALS['sql_create_trigger']);
946
        unset($GLOBALS['sql_drop_table']);
947
948
        // case 3
949
        $GLOBALS['sql_views_as_tables'] = false;
950
951
        $this->object->useSqlBackquotes(false);
952
953
        ob_start();
954
        self::assertTrue(
955
            $this->object->exportStructure(
956
                'test_db',
957
                'test_table',
958
                'create_view',
959
                'test',
960
            ),
961
        );
962
        $result = ob_get_clean();
963
964
        $sqlViews = (new ReflectionProperty(ExportSql::class, 'sqlViews'))->getValue($this->object);
965
966
        self::assertEquals('', $result);
967
        self::assertIsString($sqlViews);
968
        self::assertStringContainsString('-- Structure for view test_table', $sqlViews);
969
        self::assertStringContainsString('DROP TABLE IF EXISTS `test_table`;', $sqlViews);
970
        self::assertStringContainsString('CREATE TABLE `test_table`', $sqlViews);
971
972
        // case 4
973
        $GLOBALS['sql_views_as_tables'] = true;
974
        unset($GLOBALS['sql_if_not_exists']);
975
976
        ob_start();
977
        self::assertTrue(
978
            $this->object->exportStructure(
979
                'test_db',
980
                'test_table',
981
                'create_view',
982
                'test',
983
            ),
984
        );
985
        $result = ob_get_clean();
986
987
        self::assertIsString($result);
988
        self::assertStringContainsString('-- Structure for view test_table exported as a table', $result);
989
        self::assertStringContainsString('DROP TABLE IF EXISTS `test_table`;', $result);
990
        self::assertStringContainsString('CREATE TABLE`test_table`', $result);
991
992
        // case 5
993
        ob_start();
994
        self::assertTrue(
995
            $this->object->exportStructure(
996
                'test_db',
997
                'test_table',
998
                'stand_in',
999
                'test',
1000
            ),
1001
        );
1002
        $result = ob_get_clean();
1003
1004
        self::assertIsString($result);
1005
        self::assertStringContainsString('-- Stand-in structure for view test_table', $result);
1006
        self::assertStringContainsString('CREATE TABLE `test_table`', $result);
1007
    }
1008
1009
    public function testExportData(): void
1010
    {
1011
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1012
            ->disableOriginalConstructor()
1013
            ->getMock();
1014
1015
        $fields = [
1016
            FieldHelper::fromArray([
1017
                'type' => MYSQLI_TYPE_LONG,
1018
                'name' => 'name',
1019
                'length' => 2,
1020
            ]),
1021
            FieldHelper::fromArray([
1022
                'type' => -1,
1023
                'flags' => MYSQLI_NUM_FLAG,
1024
                'name' => 'name',
1025
                'length' => 2,
1026
            ]),
1027
            FieldHelper::fromArray([
1028
                'type' => MYSQLI_TYPE_STRING,
1029
                'name' => 'name',
1030
                'length' => 2,
1031
                'charsetnr' => 63,
1032
            ]),
1033
            FieldHelper::fromArray([
1034
                'type' => MYSQLI_TYPE_STRING,
1035
                'name' => 'name',
1036
                'length' => 2,
1037
                'charsetnr' => 63,
1038
            ]),
1039
            FieldHelper::fromArray([
1040
                'type' => MYSQLI_TYPE_BLOB,
1041
                'name' => 'name',
1042
                'length' => 2,
1043
                'charsetnr' => 63,
1044
            ]),
1045
        ];
1046
1047
        $resultStub = self::createMock(DummyResult::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::createMock() is not static, but was called statically. ( Ignorable by Annotation )

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

1047
        /** @scrutinizer ignore-call */ 
1048
        $resultStub = self::createMock(DummyResult::class);
Loading history...
1048
1049
        $dbi->expects(self::once())
1050
            ->method('getFieldsMeta')
1051
            ->with($resultStub)
1052
            ->willReturn($fields);
1053
1054
        $dbi->expects(self::once())
1055
            ->method('tryQuery')
1056
            ->with('SELECT a FROM b WHERE 1', ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)
1057
            ->willReturn($resultStub);
1058
1059
        $resultStub->expects(self::once())
1060
            ->method('numFields')
1061
            ->willReturn(5);
1062
1063
        $resultStub->expects(self::exactly(2))
1064
            ->method('fetchRow')
1065
            ->willReturn([null, 'test', '10', '6', "\x00\x0a\x0d\x1a"], []);
1066
        $dbi->expects(self::any())->method('quoteString')
1067
            ->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
1068
1069
        $tableObj = $this->getMockBuilder(Table::class)
1070
            ->disableOriginalConstructor()
1071
            ->getMock();
1072
        $tableObj->expects(self::once())
1073
            ->method('isMerge')
1074
            ->willReturn(false);
1075
        $tableObj->expects(self::once())
1076
            ->method('isView')
1077
            ->willReturn(false);
1078
1079
        $dbi->expects(self::any())
1080
            ->method('getTable')
1081
            ->willReturn($tableObj);
1082
1083
        DatabaseInterface::$instance = $dbi;
1084
        $GLOBALS['sql_compatibility'] = 'MSSQL';
1085
        $GLOBALS['sql_max_query_size'] = 50000;
1086
        $GLOBALS['sql_views_as_tables'] = true;
1087
        $GLOBALS['sql_type'] = 'INSERT';
1088
        $GLOBALS['sql_delayed'] = ' DELAYED';
1089
        $GLOBALS['sql_ignore'] = true;
1090
        $GLOBALS['sql_truncate'] = true;
1091
        $GLOBALS['sql_insert_syntax'] = 'both';
1092
        $GLOBALS['sql_hex_for_binary'] = true;
1093
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

1093
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
1094
1095
        $this->object->useSqlBackquotes(true);
1096
1097
        ob_start();
1098
        $this->object->exportData('db', 'table', 'example.com/err', 'SELECT a FROM b WHERE 1');
1099
        $result = ob_get_clean();
1100
1101
        self::assertIsString($result);
1102
1103
        self::assertStringContainsString('TRUNCATE TABLE &quot;table&quot;;', $result);
1104
1105
        self::assertStringContainsString('SET IDENTITY_INSERT &quot;table&quot; ON ;', $result);
1106
1107
        self::assertStringContainsString(
1108
            'INSERT DELAYED IGNORE INTO &quot;table&quot; (&quot;name&quot;, ' .
1109
            '&quot;name&quot;, &quot;name&quot;, &quot;name&quot;, ' .
1110
            '&quot;name&quot;) VALUES',
1111
            $result,
1112
        );
1113
1114
        self::assertStringContainsString('(NULL, \'test\', 0x3130, 0x36, 0x000a0d1a);', $result);
1115
1116
        self::assertStringContainsString('SET IDENTITY_INSERT &quot;table&quot; OFF;', $result);
1117
    }
1118
1119
    public function testExportDataWithUpdate(): void
1120
    {
1121
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1122
            ->disableOriginalConstructor()
1123
            ->getMock();
1124
1125
        $fields = [
1126
            FieldHelper::fromArray([
1127
                'type' => MYSQLI_TYPE_FLOAT,
1128
                'flags' => MYSQLI_PRI_KEY_FLAG,
1129
                'name' => 'name',
1130
                'orgname' => 'pma',
1131
                'table' => 'tbl',
1132
                'orgtable' => 'tbl',
1133
                'length' => 2,
1134
            ]),
1135
            FieldHelper::fromArray([
1136
                'type' => MYSQLI_TYPE_FLOAT,
1137
                'flags' => MYSQLI_UNIQUE_KEY_FLAG,
1138
                'name' => 'name',
1139
                'orgname' => 'pma',
1140
                'table' => 'tbl',
1141
                'orgtable' => 'tbl',
1142
                'length' => 2,
1143
            ]),
1144
        ];
1145
1146
        $resultStub = self::createMock(DummyResult::class);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::createMock() is not static, but was called statically. ( Ignorable by Annotation )

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

1146
        /** @scrutinizer ignore-call */ 
1147
        $resultStub = self::createMock(DummyResult::class);
Loading history...
1147
1148
        $dbi->expects(self::once())
1149
            ->method('getFieldsMeta')
1150
            ->with($resultStub)
1151
            ->willReturn($fields);
1152
1153
        $dbi->expects(self::once())
1154
            ->method('tryQuery')
1155
            ->with('SELECT a FROM b WHERE 1', ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)
1156
            ->willReturn($resultStub);
1157
1158
        $resultStub->expects(self::once())
1159
            ->method('numFields')
1160
            ->willReturn(2);
1161
1162
        $resultStub->expects(self::exactly(2))
1163
            ->method('fetchRow')
1164
            ->willReturn([null, null], []);
1165
1166
        $tableObj = $this->getMockBuilder(Table::class)
1167
            ->disableOriginalConstructor()
1168
            ->getMock();
1169
        $tableObj->expects(self::once())
1170
            ->method('isMerge')
1171
            ->willReturn(false);
1172
        $tableObj->expects(self::once())
1173
            ->method('isView')
1174
            ->willReturn(false);
1175
1176
        $dbi->expects(self::any())
1177
            ->method('getTable')
1178
            ->willReturn($tableObj);
1179
1180
        DatabaseInterface::$instance = $dbi;
1181
        $GLOBALS['sql_compatibility'] = 'MSSQL';
1182
        $GLOBALS['sql_views_as_tables'] = true;
1183
        $GLOBALS['sql_type'] = 'UPDATE';
1184
        $GLOBALS['sql_delayed'] = ' DELAYED';
1185
        $GLOBALS['sql_ignore'] = true;
1186
        $GLOBALS['sql_truncate'] = true;
1187
        $GLOBALS['sql_insert_syntax'] = 'both';
1188
        $GLOBALS['sql_hex_for_binary'] = true;
1189
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

1189
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
1190
1191
        $this->object->useSqlBackquotes(true);
1192
1193
        ob_start();
1194
        $this->object->exportData('db', 'table', 'example.com/err', 'SELECT a FROM b WHERE 1');
1195
        $result = ob_get_clean();
1196
1197
        self::assertIsString($result);
1198
1199
        self::assertStringContainsString(
1200
            'UPDATE IGNORE &quot;table&quot; SET &quot;name&quot; = NULL,' .
1201
            '&quot;name&quot; = NULL WHERE CONCAT(`tbl`.`pma`) IS NULL;',
1202
            $result,
1203
        );
1204
    }
1205
1206
    public function testExportDataWithIsView(): void
1207
    {
1208
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1209
            ->disableOriginalConstructor()
1210
            ->getMock();
1211
1212
        $tableObj = $this->getMockBuilder(Table::class)
1213
            ->disableOriginalConstructor()
1214
            ->getMock();
1215
        $tableObj->expects(self::once())
1216
            ->method('isMerge')
1217
            ->willReturn(false);
1218
        $tableObj->expects(self::once())
1219
            ->method('isView')
1220
            ->willReturn(true);
1221
1222
        $dbi->expects(self::any())
1223
            ->method('getTable')
1224
            ->willReturn($tableObj);
1225
1226
        DatabaseInterface::$instance = $dbi;
1227
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

1227
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
1228
        $GLOBALS['sql_views_as_tables'] = false;
1229
        $GLOBALS['sql_include_comments'] = true;
1230
        $oldVal = $GLOBALS['sql_compatibility'] ?? '';
1231
        $GLOBALS['sql_compatibility'] = 'NONE';
1232
1233
        $this->object->useSqlBackquotes(true);
1234
1235
        ob_start();
1236
        self::assertTrue(
1237
            $this->object->exportData('db', 'tbl', 'err.com', 'SELECT'),
1238
        );
1239
        $result = ob_get_clean();
1240
1241
        self::assertIsString($result);
1242
1243
        self::assertStringContainsString("-- VIEW `tbl`\n", $result);
1244
1245
        self::assertStringContainsString("-- Data: None\n", $result);
1246
1247
        // reset
1248
        $GLOBALS['sql_compatibility'] = $oldVal;
1249
    }
1250
1251
    public function testExportDataWithError(): void
1252
    {
1253
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1254
            ->disableOriginalConstructor()
1255
            ->getMock();
1256
1257
        $dbi->expects(self::once())
1258
            ->method('getError')
1259
            ->willReturn('err');
1260
1261
        $tableObj = $this->getMockBuilder(Table::class)
1262
            ->disableOriginalConstructor()
1263
            ->getMock();
1264
        $tableObj->expects(self::once())
1265
            ->method('isMerge')
1266
            ->willReturn(false);
1267
        $tableObj->expects(self::once())
1268
            ->method('isView')
1269
            ->willReturn(false);
1270
1271
        $dbi->expects(self::any())
1272
            ->method('getTable')
1273
            ->willReturn($tableObj);
1274
1275
        DatabaseInterface::$instance = $dbi;
1276
        Config::getInstance()->selectedServer['DisableIS'] = false;
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

1276
        /** @scrutinizer ignore-deprecated */ Config::getInstance()->selectedServer['DisableIS'] = false;

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...
1277
        $GLOBALS['sql_views_as_tables'] = true;
1278
        $GLOBALS['sql_include_comments'] = true;
1279
1280
        ob_start();
1281
        self::assertTrue(
1282
            $this->object->exportData('db', 'table', 'err.com', 'SELECT'),
1283
        );
1284
        $result = ob_get_clean();
1285
1286
        self::assertIsString($result);
1287
1288
        self::assertStringContainsString('-- Error reading data for table db.table: err', $result);
1289
    }
1290
1291
    public function testMakeCreateTableMSSQLCompatible(): void
1292
    {
1293
        $query = "CREATE TABLE IF NOT EXISTS (\" date DEFAULT NULL,\n"
1294
            . "\" date DEFAULT NULL\n\" date NOT NULL,\n\" date NOT NULL\n,"
1295
            . " \" date NOT NULL DEFAULT 'asd',"
1296
            . " ) unsigned NOT NULL\n, ) unsigned NOT NULL,\n"
1297
            . " ) unsigned DEFAULT NULL\n, ) unsigned DEFAULT NULL,\n"
1298
            . " ) unsigned NOT NULL DEFAULT 'dsa',\n"
1299
            . " \" int(10) DEFAULT NULL,\n"
1300
            . " \" tinyint(0) DEFAULT NULL\n"
1301
            . " \" smallint(10) NOT NULL,\n"
1302
            . " \" bigint(0) NOT NULL\n"
1303
            . " \" bigint(0) NOT NULL DEFAULT '12'\n"
1304
            . " \" float(22,2,) DEFAULT NULL,\n"
1305
            . " \" double DEFAULT NULL\n"
1306
            . " \" float(22,2,) NOT NULL,\n"
1307
            . " \" double NOT NULL\n"
1308
            . " \" double NOT NULL DEFAULT '213'\n";
1309
1310
        $method = new ReflectionMethod(ExportSql::class, 'makeCreateTableMSSQLCompatible');
1311
        $result = $method->invoke($this->object, $query);
1312
1313
        self::assertSame(
1314
            "CREATE TABLE (\" datetime DEFAULT NULL,\n" .
1315
            "\" datetime DEFAULT NULL\n" .
1316
            "\" datetime NOT NULL,\n" .
1317
            "\" datetime NOT NULL\n" .
1318
            ", \" datetime NOT NULL DEFAULT 'asd', ) NOT NULL\n" .
1319
            ", ) NOT NULL,\n" .
1320
            " ) DEFAULT NULL\n" .
1321
            ", ) DEFAULT NULL,\n" .
1322
            " ) NOT NULL DEFAULT 'dsa',\n" .
1323
            " \" int DEFAULT NULL,\n" .
1324
            " \" tinyint DEFAULT NULL\n" .
1325
            " \" smallint NOT NULL,\n" .
1326
            " \" bigint NOT NULL\n" .
1327
            " \" bigint NOT NULL DEFAULT '12'\n" .
1328
            " \" float DEFAULT NULL,\n" .
1329
            " \" float DEFAULT NULL\n" .
1330
            " \" float NOT NULL,\n" .
1331
            " \" float NOT NULL\n" .
1332
            " \" float NOT NULL DEFAULT '213'\n",
1333
            $result,
1334
        );
1335
    }
1336
1337
    public function testInitAlias(): void
1338
    {
1339
        $aliases = [
1340
            'a' => [
1341
                'alias' => 'aliastest',
1342
                'tables' => ['foo' => ['alias' => 'qwerty'], 'bar' => ['alias' => 'f']],
1343
            ],
1344
        ];
1345
        $db = 'a';
1346
        $table = null;
1347
1348
        $this->object->initAlias($aliases, $db, $table);
1349
        self::assertSame('aliastest', $db);
1350
        self::assertNull($table);
1351
1352
        $db = 'foo';
1353
        $table = 'qwerty';
1354
1355
        $this->object->initAlias($aliases, $db, $table);
1356
        self::assertSame('foo', $db);
1357
        self::assertSame('qwerty', $table);
1358
1359
        $db = 'a';
1360
        $table = 'foo';
1361
1362
        $this->object->initAlias($aliases, $db, $table);
1363
        self::assertSame('aliastest', $db);
1364
        self::assertSame('qwerty', $table);
1365
    }
1366
1367
    public function testGetAlias(): void
1368
    {
1369
        $aliases = [
1370
            'a' => [
1371
                'alias' => 'aliastest',
1372
                'tables' => [
1373
                    'foo' => ['alias' => 'qwerty', 'columns' => ['baz' => 'p', 'pqr' => 'pphymdain']],
1374
                    'bar' => ['alias' => 'f', 'columns' => ['xy' => 'n']],
1375
                ],
1376
            ],
1377
        ];
1378
1379
        self::assertSame(
1380
            'f',
1381
            $this->object->getAlias($aliases, 'bar'),
1382
        );
1383
1384
        self::assertSame(
1385
            'aliastest',
1386
            $this->object->getAlias($aliases, 'a'),
1387
        );
1388
1389
        self::assertSame(
1390
            'pphymdain',
1391
            $this->object->getAlias($aliases, 'pqr'),
1392
        );
1393
1394
        self::assertSame(
1395
            '',
1396
            $this->object->getAlias($aliases, 'abc'),
1397
        );
1398
    }
1399
1400
    public function testReplaceWithAlias(): void
1401
    {
1402
        $aliases = [
1403
            'a' => [
1404
                'alias' => 'aliastest',
1405
                'tables' => [
1406
                    'foo' => ['alias' => 'bartest', 'columns' => ['baz' => 'p', 'pqr' => 'pphymdain']],
1407
                    'bar' => ['alias' => 'f', 'columns' => ['xy' => 'n']],
1408
                ],
1409
            ],
1410
        ];
1411
1412
        $db = 'a';
1413
        $sqlQuery = "CREATE TABLE IF NOT EXISTS foo (\n"
1414
            . "baz tinyint(3) unsigned NOT NULL COMMENT 'Primary Key',\n"
1415
            . 'xyz varchar(255) COLLATE latin1_general_ci NOT NULL '
1416
            . "COMMENT 'xyz',\n"
1417
            . 'pqr varchar(10) COLLATE latin1_general_ci NOT NULL '
1418
            . "COMMENT 'pqr',\n"
1419
            . 'CONSTRAINT fk_om_dept FOREIGN KEY (baz) '
1420
            . "REFERENCES dept_master (baz)\n"
1421
            . ') ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE='
1422
            . "latin1_general_ci COMMENT='List' AUTO_INCREMENT=5";
1423
        $result = $this->object->replaceWithAliases(null, $sqlQuery, $aliases, $db);
1424
1425
        self::assertSame(
1426
            "CREATE TABLE IF NOT EXISTS `bartest` (\n" .
1427
            "  `p` tinyint(3) UNSIGNED NOT NULL COMMENT 'Primary Key',\n" .
1428
            "  `xyz` varchar(255) COLLATE latin1_general_ci NOT NULL COMMENT 'xyz',\n" .
1429
            "  `pphymdain` varchar(10) COLLATE latin1_general_ci NOT NULL COMMENT 'pqr',\n" .
1430
            "  CONSTRAINT `fk_om_dept` FOREIGN KEY (`p`) REFERENCES dept_master (`baz`)\n" .
1431
            ") ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='List'",
1432
            $result,
1433
        );
1434
1435
        $result = $this->object->replaceWithAliases(null, $sqlQuery, [], '');
1436
1437
        self::assertSame(
1438
            "CREATE TABLE IF NOT EXISTS foo (\n" .
1439
            "  `baz` tinyint(3) UNSIGNED NOT NULL COMMENT 'Primary Key',\n" .
1440
            "  `xyz` varchar(255) COLLATE latin1_general_ci NOT NULL COMMENT 'xyz',\n" .
1441
            "  `pqr` varchar(10) COLLATE latin1_general_ci NOT NULL COMMENT 'pqr',\n" .
1442
            "  CONSTRAINT `fk_om_dept` FOREIGN KEY (`baz`) REFERENCES dept_master (`baz`)\n" .
1443
            ") ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='List'",
1444
            $result,
1445
        );
1446
1447
        $sqlQuery = 'CREATE TRIGGER `BEFORE_bar_INSERT` '
1448
            . 'BEFORE INSERT ON `bar` '
1449
            . 'FOR EACH ROW BEGIN '
1450
            . 'SET @cnt=(SELECT count(*) FROM bar WHERE '
1451
            . 'xy=NEW.xy AND id=NEW.id AND '
1452
            . 'abc=NEW.xy LIMIT 1); '
1453
            . 'IF @cnt<>0 THEN '
1454
            . 'SET NEW.xy=1; '
1455
            . 'END IF; END';
1456
        $result = $this->object->replaceWithAliases('$$', $sqlQuery, $aliases, $db);
1457
1458
        self::assertSame(
1459
            'CREATE TRIGGER `BEFORE_bar_INSERT` BEFORE INSERT ON `f` FOR EACH ROW BEGIN ' .
1460
            'SET @cnt=(SELECT count(*) FROM `f` WHERE `n`=NEW.`n` AND id=NEW.id AND abc=NEW.`n` LIMIT 1); ' .
1461
            'IF @cnt<>0 THEN ' .
1462
            'SET NEW.`n`=1; ' .
1463
            'END IF; ' .
1464
            'END',
1465
            $result,
1466
        );
1467
1468
        $sqlQuery = <<<'SQL'
1469
CREATE FUNCTION `HTML_UnEncode`(`x` TEXT CHARSET utf8) RETURNS text CHARSET utf8
1470
BEGIN
1471
1472
DECLARE TextString TEXT ;
1473
SET TextString = x ;
1474
1475
#quotation mark
1476
IF INSTR( x , '&quot;' )
1477
THEN SET TextString = REPLACE(TextString, '&quot;','"') ;
1478
END IF ;
1479
1480
#apostrophe
1481
IF INSTR( x , '&apos;' )
1482
THEN SET TextString = REPLACE(TextString, '&apos;','"') ;
1483
END IF ;
1484
1485
RETURN TextString ;
1486
1487
END
1488
SQL;
1489
1490
        $result = $this->object->replaceWithAliases('$$', $sqlQuery, $aliases, $db);
1491
1492
        $expectedQuery = <<<'SQL'
1493
CREATE FUNCTION `HTML_UnEncode` (`x` TEXT CHARSET utf8) RETURNS TEXT CHARSET utf8  BEGIN
1494
1495
DECLARE TextString TEXT ;
1496
SET TextString = x ;
1497
1498
#quotation mark
1499
IF INSTR( x , '&quot;' )
1500
THEN SET TextString = REPLACE(TextString, '&quot;','"') ;
1501
END IF ;
1502
1503
#apostrophe
1504
IF INSTR( x , '&apos;' )
1505
THEN SET TextString = REPLACE(TextString, '&apos;','"') ;
1506
END IF ;
1507
1508
RETURN TextString ;
1509
1510
END
1511
SQL;
1512
1513
        self::assertSame($expectedQuery, $result);
1514
    }
1515
}
1516