Completed
Push — master ( b46f20...826d41 )
by Michael
11s
created

Tables::alterColumn()   D

Complexity

Conditions 9
Paths 14

Size

Total Lines 38
Code Lines 23

Duplication

Lines 14
Ratio 36.84 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 23
c 1
b 0
f 0
nc 14
nop 4
dl 14
loc 38
rs 4.909
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
namespace Xmf\Database;
13
14
use Xmf\Language;
15
16
/**
17
 * Xmf\Database\Tables
18
 *
19
 * inspired by Yii CDbMigration
20
 *
21
 * Build a work queue of database changes needed to implement new and
22
 * changed tables. Define table(s) you are dealing with and any desired
23
 * change(s). If the changes are already in place (i.e. the new column
24
 * already exists) no work is added. Then executeQueue() to process the
25
 * whole set.
26
 *
27
 * @category  Xmf\Database\Tables
28
 * @package   Xmf
29
 * @author    Richard Griffith <[email protected]>
30
 * @copyright 2011-2016 XOOPS Project (http://xoops.org)
31
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
32
 * @link      http://xoops.org
33
 */
34
class Tables
35
{
36
    /**
37
     * @var \XoopsDatabase
38
     */
39
    protected $db;
40
41
    /**
42
     * @var string
43
     */
44
    protected $databaseName;
45
46
    /**
47
     * @var array Tables
48
     */
49
    protected $tables;
50
51
    /**
52
     * @var array Work queue
53
     */
54
    protected $queue;
55
56
    /**
57
     * @var string last error message
58
     */
59
    protected $lastError;
60
61
    /**
62
     * @var int last error number
63
     */
64
    protected $lastErrNo;
65
66
    /**
67
     * Constructor
68
     *
69
     */
70
    public function __construct()
71
    {
72
        Language::load('xmf');
73
74
        $this->db = \XoopsDatabaseFactory::getDatabaseConnection();
75
        $this->databaseName = XOOPS_DB_NAME;
76
        $this->resetQueue();
77
    }
78
79
    /**
80
     * Return a table name, prefixed with site table prefix
81
     *
82
     * @param string $table table name to contain prefix
83
     *
84
     * @return string table name with prefix
85
     */
86
    protected function name($table)
87
    {
88
        return $this->db->prefix($table);
89
    }
90
91
    /**
92
     * Add new column for table to the work queue
93
     *
94
     * @param string $table      table to contain the column
95
     * @param string $column     name of column to add
96
     * @param string $attributes column_definition
97
     *
98
     * @return bool true if no errors, false if errors encountered
99
     */
100
    public function addColumn($table, $column, $attributes)
101
    {
102
        $columnDef = array(
103
            'name' => $column,
104
            'attributes' => $attributes
105
        );
106
107
        // Find table def.
108
        if (isset($this->tables[$table])) {
109
            $tableDef = &$this->tables[$table];
110
            // Is this on a table we are adding?
111
            if (isset($tableDef['create']) && $tableDef['create']) {
112
                array_push($tableDef['columns'], $columnDef);
113
            } else {
114 View Code Duplication
                foreach ($tableDef['columns'] as $col) {
115
                    if (strcasecmp($col['name'], $column) == 0) {
116
                        return true;
117
                    }
118
                }
119
                $this->queue[] = "ALTER TABLE `{$tableDef['name']}`"
120
                    . " ADD COLUMN `{$column}` {$columnDef['attributes']}";
121
                array_push($tableDef['columns'], $columnDef);
122
            }
123
        } else {
124
            return $this->tableNotEstablished();
125
        }
126
127
        return true; // exists or is added to queue
128
    }
129
130
    /**
131
     * Add new primary key definition for table to work queue
132
     *
133
     * @param string $table  table
134
     * @param string $column column or comma separated list of columns
135
     *                       to use as primary key
136
     *
137
     * @return bool true if no errors, false if errors encountered
138
     */
139
    public function addPrimaryKey($table, $column)
140
    {
141
        $columns = str_getcsv(str_replace(' ', '', $column));
142
        $columnList = '';
143
        $firstComma = '';
144
        foreach ($columns as $col) {
145
            $columnList .= "{$firstComma}`{$col}`";
146
            $firstComma = ', ';
147
        }
148
        if (isset($this->tables[$table])) {
149
            if (isset($this->tables[$table]['create']) && $this->tables[$table]['create']) {
150
                $this->tables[$table]['keys']['PRIMARY']['columns'] = $columnList;
151
            } else {
152
                $this->queue[] = "ALTER TABLE `{$this->tables[$table]['name']}` ADD PRIMARY KEY({$columnList})";
153
            }
154
        } else {
155
            return $this->tableNotEstablished();
156
        }
157
158
        return true;
159
    }
160
161
    /**
162
     * Add new index definition for index to work queue
163
     *
164
     * @param string $name   name of index to add
165
     * @param string $table  table indexed
166
     * @param string $column column or a comma separated list of columns
167
     *                        to use as the key
168
     * @param bool   $unique true if index is to be unique
169
     *
170
     * @return bool true if no errors, false if errors encountered
171
     */
172
    public function addIndex($name, $table, $column, $unique = false)
173
    {
174
        $columns = str_getcsv(str_replace(' ', '', $column));
175
        $columnList = '';
176
        $firstComma = '';
177
        foreach ($columns as $col) {
178
            $columnList .= "{$firstComma}`{$col}`";
179
            $firstComma = ', ';
180
        }
181
        if (isset($this->tables[$table])) {
182
            if (isset($this->tables[$table]['create']) && $this->tables[$table]['create']) {
183
                $this->tables[$table]['keys'][$name]['columns'] = $columnList;
184
                $this->tables[$table]['keys'][$name]['unique'] = (bool) $unique;
185
            } else {
186
                $add = ($unique ? 'ADD UNIQUE INDEX' : 'ADD INDEX');
187
                $this->queue[] = "ALTER TABLE `{$this->tables[$table]['name']}` {$add} `{$name}` ({$columnList})";
188
            }
189
        } else {
190
            return $this->tableNotEstablished();
191
        }
192
193
        return true;
194
    }
195
196
    /**
197
     * Load table schema from database, or starts new empty schema if
198
     * table does not exist
199
     *
200
     * @param string $table table
201
     *
202
     * @return bool true if no errors, false if errors encountered
203
     */
204
    public function addTable($table)
205
    {
206
        if (isset($this->tables[$table])) {
207
            return true;
208
        }
209
        $tableDef = $this->getTable($table);
210
        if (is_array($tableDef)) {
211
            $this->tables[$table] = $tableDef;
212
213
            return true;
214
        } else {
215
            if ($tableDef === true) {
216
                $tableDef = array(
217
                    'name' => $this->name($table),
218
                    'options' => 'ENGINE=InnoDB',
219
                    'columns' => array(),
220
                    'keys' => array(),
221
                    'create' => true,
222
                );
223
                $this->tables[$table] = $tableDef;
224
225
                $this->queue[] = array('createtable' => $table);
226
227
                return true;
228
            } else {
229
                return false;
230
            }
231
        }
232
    }
233
234
    /**
235
     * AddTable only if it exists
236
     *
237
     * @param string $table table
238
     *
239
     * @return bool true if table exists, false otherwise
240
     */
241
    public function useTable($table)
242
    {
243
        if (isset($this->tables[$table])) {
244
            return true;
245
        }
246
        $tableDef = $this->getTable($table);
247
        if (is_array($tableDef)) {
248
            $this->tables[$table] = $tableDef;
249
            return true;
250
        }
251
        return false;
252
    }
253
254
    /**
255
     * Get column attributes
256
     *
257
     * @param string $table  table containing the column
258
     * @param string $column column to alter
259
     *
260
     * @return string|bool attribute string, or false if error encountered
261
     */
262
    public function getColumnAttributes($table, $column)
263
    {
264
        // Find table def.
265
        if (isset($this->tables[$table])) {
266
            $tableDef = $this->tables[$table];
267
            // loop thru and find the column
268 View Code Duplication
            foreach ($tableDef['columns'] as $col) {
269
                if (strcasecmp($col['name'], $column) === 0) {
270
                    return $col['attributes'];
271
                }
272
            }
273
        }
274
275
        return false;
276
    }
277
278
    /**
279
     * Get indexes for a table
280
     *
281
     * @param string $table get indexes for this named table
282
     *
283
     * @return array|bool array of indexes, or false if error encountered
284
     */
285
    public function getTableIndexes($table)
286
    {
287
        // Find table def.
288
        if (isset($this->tables[$table]) && isset($this->tables[$table]['keys'])) {
289
            return $this->tables[$table]['keys'];
290
        }
291
292
        return false;
293
    }
294
295
    /**
296
     * Add alter column operation to the work queue
297
     *
298
     * @param string $table      table containing the column
299
     * @param string $column     column to alter
300
     * @param string $attributes new column_definition
301
     * @param string $newName    new name for column, blank to keep same
302
     *
303
     * @return bool true if no errors, false if errors encountered
304
     */
305
    public function alterColumn($table, $column, $attributes, $newName = '')
306
    {
307
        if (empty($newName)) {
308
            $newName = $column;
309
        }
310
        // Find table def.
311
        if (isset($this->tables[$table])) {
312
            $tableDef = &$this->tables[$table];
313
            // Is this on a table we are adding?
314
            if (isset($tableDef['create']) && $tableDef['create']) {
315
                // loop thru and find the column
316 View Code Duplication
                foreach ($tableDef['columns'] as &$col) {
317
                    if (strcasecmp($col['name'], $column) == 0) {
318
                        $col['name'] = $newName;
319
                        $col['attributes'] = $attributes;
320
                        break;
321
                    }
322
                }
323
324
                return true;
325
            } else {
326
                $this->queue[] = "ALTER TABLE `{$tableDef['name']}` " .
327
                    "CHANGE COLUMN `{$column}` `{$newName}` {$attributes} ";
328
                // loop thru and find the column
329 View Code Duplication
                foreach ($tableDef['columns'] as &$col) {
330
                    if (strcasecmp($col['name'], $column) == 0) {
331
                        $col['name'] = $newName;
332
                        $col['attributes'] = $attributes;
333
                        break;
334
                    }
335
                }
336
            }
337
        } else {
338
            return $this->tableNotEstablished();
339
        }
340
341
        return true;
342
    }
343
344
    /**
345
     * Loads table schema from database, and adds newTable with that
346
     * schema to the queue
347
     *
348
     * @param string $table    existing table
349
     * @param string $newTable new table
350
     * @param bool   $withData true to copy data, false for schema only
351
     *
352
     * @return bool true if no errors, false if errors encountered
353
     */
354
    public function copyTable($table, $newTable, $withData = false)
355
    {
356
        if (isset($this->tables[$newTable])) {
357
            return true;
358
        }
359
        $tableDef = $this->getTable($table);
360
        $copy = $this->name($newTable);
361
        $original = $this->name($table);
362
363
        if (is_array($tableDef)) {
364
            $tableDef['name'] = $copy;
365
            if ($withData) {
366
                $this->queue[] = "CREATE TABLE `{$copy}` LIKE `{$original}` ;";
367
                $this->queue[] = "INSERT INTO `{$copy}` SELECT * FROM `{$original}` ;";
368
            } else {
369
                $tableDef['create'] = true;
370
                $this->queue[] = array('createtable' => $newTable);
371
            }
372
            $this->tables[$newTable] = $tableDef;
373
374
            return true;
375
        } else {
376
            return false;
377
        }
378
    }
379
380
    /**
381
     * Add drop column operation to the work queue
382
     *
383
     * @param string $table  table containing the column
384
     * @param string $column column to drop
385
     *
386
     * @return bool true if no errors, false if errors encountered
387
     */
388 View Code Duplication
    public function dropColumn($table, $column)
389
    {
390
        // Find table def.
391
        if (isset($this->tables[$table])) {
392
            $tableDef = $this->tables[$table];
393
            $this->queue[] = "ALTER TABLE `{$tableDef['name']}` DROP COLUMN `{$column}`";
394
        } else {
395
            return $this->tableNotEstablished();
396
        }
397
398
        return true;
399
    }
400
401
    /**
402
     * Add drop index operation to the work queue
403
     *
404
     * @param string $name  name of index to drop
405
     * @param string $table table indexed
406
     *
407
     * @return bool true if no errors, false if errors encountered
408
     */
409 View Code Duplication
    public function dropIndex($name, $table)
410
    {
411
        if (isset($this->tables[$table])) {
412
            $tableDef = $this->tables[$table];
413
            $this->queue[] = "ALTER TABLE `{$tableDef['name']}` DROP INDEX `{$name}`";
414
        } else {
415
            return $this->tableNotEstablished();
416
        }
417
418
        return true;
419
    }
420
421
    /**
422
     * Add drop for all (non-PRIMARY) keys for a table to the work
423
     * queue. This can be used to clean up indexes with automatic names.
424
     *
425
     * @param string $table table indexed
426
     *
427
     * @return bool true if no errors, false if errors encountered
428
     */
429
    public function dropIndexes($table)
430
    {
431
        // Find table def.
432
        if (isset($this->tables[$table])) {
433
            $tableDef = &$this->tables[$table];
434
            // Is this on a table we are adding?
435
            if (isset($tableDef['create']) && $tableDef['create']) {
436
                // strip everything but the PRIMARY from definition
437 View Code Duplication
                foreach ($tableDef['keys'] as $keyName => $key) {
438
                    if ($keyName !== 'PRIMARY') {
439
                        unset($tableDef['keys'][$keyName]);
440
                    }
441
                }
442
            } else {
443
                // build drops to strip everything but the PRIMARY
444 View Code Duplication
                foreach ($tableDef['keys'] as $keyName => $key) {
445
                    if ($keyName !== 'PRIMARY') {
446
                        $this->queue[] = "ALTER TABLE `{$tableDef['name']}` DROP INDEX {$keyName}";
447
                    }
448
                }
449
            }
450
        } else {
451
            return $this->tableNotEstablished();
452
        }
453
454
        return true;
455
    }
456
457
    /**
458
     * Add drop of PRIMARY key for a table to the work queue
459
     *
460
     * @param string $table table
461
     *
462
     * @return bool true if no errors, false if errors encountered
463
     */
464 View Code Duplication
    public function dropPrimaryKey($table)
465
    {
466
        if (isset($this->tables[$table])) {
467
            $tableDef = $this->tables[$table];
468
            $this->queue[] = "ALTER TABLE `{$tableDef['name']}` DROP PRIMARY KEY ";
469
        } else {
470
            return $this->tableNotEstablished();
471
        }
472
473
        return true;
474
    }
475
476
    /**
477
     * Add drop of table to the work queue
478
     *
479
     * @param string $table table
480
     *
481
     * @return bool true if no errors, false if errors encountered
482
     */
483 View Code Duplication
    public function dropTable($table)
484
    {
485
        if (isset($this->tables[$table])) {
486
            $tableDef = $this->tables[$table];
487
            $this->queue[] = "DROP TABLE `{$tableDef['name']}` ";
488
            unset($this->tables[$table]);
489
        }
490
        // no table is not an error since we are dropping it anyway
491
        return true;
492
    }
493
494
495
    /**
496
     * Add rename table operation to the work queue
497
     *
498
     * @param string $table   table
499
     * @param string $newName new table name
500
     *
501
     * @return bool true if no errors, false if errors encountered
502
     */
503
    public function renameTable($table, $newName)
504
    {
505
        if (isset($this->tables[$table])) {
506
            $tableDef = $this->tables[$table];
507
            $newTable = $this->name($newName);
508
            $this->queue[] = "ALTER TABLE `{$tableDef['name']}` RENAME TO `{$newTable}`";
509
            $tableDef['name'] = $newTable;
510
            $this->tables[$newName] = $tableDef;
511
            unset($this->tables[$table]);
512
        } else {
513
            return $this->tableNotEstablished();
514
        }
515
516
        return true;
517
    }
518
519
    /**
520
     * Add alter table table_options (ENGINE, DEFAULT CHARSET, etc.)
521
     * to work queue
522
     *
523
     * @param string $table   table
524
     * @param string $options table_options
525
     *
526
     * @return bool true if no errors, false if errors encountered
527
     */
528
    public function setTableOptions($table, $options)
529
    {
530
        if (isset($this->tables[$table])) {
531
            $tableDef = &$this->tables[$table];
532
            // Is this on a table we are adding?
533
            if (isset($tableDef['create']) && $tableDef['create']) {
534
                $tableDef['options'] = $options;
535
                return true;
536
            } else {
537
                $this->queue[] = "ALTER TABLE `{$tableDef['name']}` {$options} ";
538
                $tableDef['options'] = $options;
539
                return true;
540
            }
541
        } else {
542
            return $this->tableNotEstablished();
543
        }
544
    }
545
546
547
    /**
548
     * Clear the work queue
549
     *
550
     * @return void
551
     */
552
    public function resetQueue()
553
    {
554
        $this->tables = array();
555
        $this->queue  = array();
556
    }
557
558
    /**
559
     * Executes the work queue
560
     *
561
     * @param bool $force true to force updates even if this is a 'GET' request
562
     *
563
     * @return bool true if no errors, false if errors encountered
564
     */
565
    public function executeQueue($force = false)
566
    {
567
        $this->expandQueue();
568
        foreach ($this->queue as &$ddl) {
569 View Code Duplication
            if (is_array($ddl)) {
570
                if (isset($ddl['createtable'])) {
571
                    $ddl = $this->renderTableCreate($ddl['createtable']);
572
                }
573
            }
574
            $result = $this->execSql($ddl, $force);
575
            if (!$result) {
576
                $this->lastError = $this->db->error();
577
                $this->lastErrNo = $this->db->errno();
578
579
                return false;
580
            }
581
        }
582
583
        return true;
584
    }
585
586
587
    /**
588
     * Create a DELETE statement and add it to the work queue
589
     *
590
     * @param string                 $table    table
591
     * @param string|CriteriaElement $criteria string where clause or object criteria
592
     *
593
     * @return bool true if no errors, false if errors encountered
594
     */
595
    public function delete($table, $criteria)
596
    {
597
        if (isset($this->tables[$table])) {
598
            $tableDef = $this->tables[$table];
599
            $where = '';
600
            if (is_scalar($criteria)) {
601
                $where = $criteria;
602
            } elseif (is_object($criteria)) {
603
                $where = $criteria->renderWhere();
604
            }
605
            $this->queue[] = "DELETE FROM `{$tableDef['name']}` {$where}";
606
        } else {
607
            return $this->tableNotEstablished();
608
        }
609
610
        return true;
611
    }
612
613
    /**
614
     * Create an INSERT SQL statement and add it to the work queue.
615
     *
616
     * @param string  $table      table
617
     * @param array   $columns    array of 'column'=>'value' entries
618
     * @param boolean $quoteValue true to quote values, false if caller handles quoting
619
     *
620
     * @return boolean true if no errors, false if errors encountered
621
     */
622
    public function insert($table, $columns, $quoteValue = true)
623
    {
624
        if (isset($this->tables[$table])) {
625
            $tableDef = $this->tables[$table];
626
            $colSql = '';
627
            $valSql = '';
628 View Code Duplication
            foreach ($tableDef['columns'] as $col) {
629
                $comma = empty($colSql) ? '' : ', ';
630
                if (isset($columns[$col['name']])) {
631
                    $colSql .= "{$comma}`{$col['name']}`";
632
                    $valSql .= $comma
633
                        . ($quoteValue ? $this->db->quote($columns[$col['name']]) : $columns[$col['name']]);
634
                }
635
            }
636
            $sql = "INSERT INTO `{$tableDef['name']}` ({$colSql}) VALUES({$valSql})";
637
            $this->queue[] = $sql;
638
639
            return true;
640
        } else {
641
            return $this->tableNotEstablished();
642
        }
643
    }
644
645
    /**
646
     * Create an UPDATE SQL statement and add it to the work queue
647
     *
648
     * @param string                 $table      table
649
     * @param array                  $columns    array of 'column'=>'value' entries
650
     * @param string|CriteriaElement $criteria   string where clause or object criteria
651
     * @param boolean                $quoteValue true to quote values, false if caller handles quoting
652
     *
653
     * @return boolean true if no errors, false if errors encountered
654
     */
655
    public function update($table, $columns, $criteria, $quoteValue = true)
656
    {
657
        if (isset($this->tables[$table])) {
658
            $tableDef = $this->tables[$table];
659
            $where = '';
660
            if (is_scalar($criteria)) {
661
                $where = $criteria;
662
            } elseif (is_object($criteria)) {
663
                $where = $criteria->renderWhere();
664
            }
665
            $colSql = '';
666 View Code Duplication
            foreach ($tableDef['columns'] as $col) {
667
                $comma = empty($colSql) ? '' : ', ';
668
                if (isset($columns[$col['name']])) {
669
                    $colSql .= "{$comma}`{$col['name']}` = "
670
                        . ($quoteValue ? $this->db->quote($columns[$col['name']]) : $columns[$col['name']]);
671
                }
672
            }
673
            $sql = "UPDATE `{$tableDef['name']}` SET {$colSql} {$where}";
674
            $this->queue[] = $sql;
675
676
            return true;
677
        } else {
678
            return $this->tableNotEstablished();
679
        }
680
    }
681
682
    /**
683
     * Add statement to remove all rows from a table to the work queue
684
     *
685
     * @param string $table table
686
     *
687
     * @return bool true if no errors, false if errors encountered
688
     */
689 View Code Duplication
    public function truncate($table)
690
    {
691
        if (isset($this->tables[$table])) {
692
            $tableDef = $this->tables[$table];
693
            $this->queue[] = "TRUNCATE TABLE `{$tableDef['name']}`";
694
        } else {
695
            return $this->tableNotEstablished();
696
        }
697
698
        return true;
699
    }
700
701
702
703
    /**
704
     * return SQL to create the table
705
     *
706
     * This method does NOT modify the work queue
707
     *
708
     * @param string $table    table
709
     * @param bool   $prefixed true to return with table name prefixed
710
     *
711
     * @return string|false string SQL to create table, or false if errors encountered
0 ignored issues
show
Documentation introduced by
Should the return type not be string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
712
     */
713
    protected function renderTableCreate($table, $prefixed = false)
714
    {
715
        if (isset($this->tables[$table])) {
716
            $tableDef = $this->tables[$table];
717
            $tableName = ($prefixed ? $tableDef['name'] : $table);
718
            $sql = "CREATE TABLE `{$tableName}` (";
719
            $firstComma = '';
720
            foreach ($tableDef['columns'] as $col) {
721
                $sql .= "{$firstComma}\n    `{$col['name']}`  {$col['attributes']}";
722
                $firstComma = ',';
723
            }
724
            $keySql = '';
725
            foreach ($tableDef['keys'] as $keyName => $key) {
726
                if ($keyName === 'PRIMARY') {
727
                    $keySql .= ",\n  PRIMARY KEY ({$key['columns']})";
728
                } else {
729
                    $unique = $key['unique'] ? 'UNIQUE ' : '';
730
                    $keySql .= ",\n  {$unique}KEY {$keyName} ({$key['columns']})";
731
                }
732
            }
733
            $sql .= $keySql;
734
            $sql .= "\n) {$tableDef['options']}";
735
736
            return $sql;
737
        } else {
738
            return $this->tableNotEstablished();
739
        }
740
    }
741
742
    /**
743
     * execute an SQL statement
744
     *
745
     * @param string $sql   SQL statement to execute
746
     * @param bool   $force true to use force updates even in safe requests
747
     *
748
     * @return mixed result resource if no error,
749
     *               true if no error but no result
750
     *               false if error encountered.
751
     *               Any error message is in $this->lastError;
752
     */
753
    protected function execSql($sql, $force = false)
754
    {
755
        if ($force) {
756
            $result = $this->db->queryF($sql);
757
        } else {
758
            $result = $this->db->query($sql);
759
        }
760
761
        if (!$result) {
762
            $this->lastError = $this->db->error();
763
            $this->lastErrNo = $this->db->errno();
764
        }
765
766
        return $result;
767
    }
768
769
    /**
770
     * fetch the next row of a result set
771
     *
772
     * @param resource $result as returned by query
773
     *
774
     * @return mixed false on error
775
     */
776
    protected function fetch($result)
777
    {
778
        return $this->db->fetchArray($result);
779
    }
780
781
    /**
782
     * get table definition from INFORMATION_SCHEMA
783
     *
784
     * @param string $table table
785
     *
786
     * @return array|bool table definition array if table exists, true if table not defined, or
787
     *                    false on error. Error message in $this->lastError;
788
     */
789
    protected function getTable($table)
790
    {
791
        $tableDef = array();
792
793
        $sql  = 'SELECT TABLE_NAME, ENGINE, CHARACTER_SET_NAME ';
794
        $sql .= ' FROM `INFORMATION_SCHEMA`.`TABLES` t, ';
795
        $sql .= ' `INFORMATION_SCHEMA`.`COLLATIONS` c ';
796
        $sql .= ' WHERE t.TABLE_SCHEMA = \'' . $this->databaseName . '\' ';
797
        $sql .= ' AND t.TABLE_NAME = \'' . $this->name($table) . '\' ';
798
        $sql .= ' AND t.TABLE_COLLATION  = c.COLLATION_NAME ';
799
800
        $result = $this->execSql($sql);
801
        if (!$result) {
802
            return false;
803
        }
804
        $tableSchema = $this->fetch($result);
805
        if (empty($tableSchema)) {
806
            return true;
807
        }
808
        $tableDef['name'] = $tableSchema['TABLE_NAME'];
809
        $tableDef['options'] = 'ENGINE=' . $tableSchema['ENGINE'] . ' '
810
            . 'DEFAULT CHARSET=' . $tableSchema['CHARACTER_SET_NAME'];
811
812
        $sql  = 'SELECT * ';
813
        $sql .= ' FROM `INFORMATION_SCHEMA`.`COLUMNS` ';
814
        $sql .= ' WHERE TABLE_SCHEMA = \'' . $this->databaseName . '\' ';
815
        $sql .= ' AND TABLE_NAME = \'' . $this->name($table) . '\' ';
816
        $sql .= ' ORDER BY `ORDINAL_POSITION` ';
817
818
        $result = $this->execSql($sql);
819
820
        while ($column = $this->fetch($result)) {
821
            $attributes = ' ' . $column['COLUMN_TYPE'] . ' '
822
                . (($column['IS_NULLABLE'] === 'NO') ? ' NOT NULL ' : '')
823
                . (($column['COLUMN_DEFAULT'] === null) ? '' : " DEFAULT '" . $column['COLUMN_DEFAULT'] . "' ")
824
                . $column['EXTRA'];
825
826
            $columnDef = array(
827
                'name' => $column['COLUMN_NAME'],
828
                'attributes' => $attributes
829
            );
830
831
            $tableDef['columns'][] = $columnDef;
832
        };
833
834
        $sql  = 'SELECT `INDEX_NAME`, `SEQ_IN_INDEX`, `NON_UNIQUE`, ';
835
        $sql .= ' `COLUMN_NAME`, `SUB_PART` ';
836
        $sql .= ' FROM `INFORMATION_SCHEMA`.`STATISTICS` ';
837
        $sql .= ' WHERE TABLE_SCHEMA = \'' . $this->databaseName . '\' ';
838
        $sql .= ' AND TABLE_NAME = \'' . $this->name($table) . '\' ';
839
        $sql .= ' ORDER BY `INDEX_NAME`, `SEQ_IN_INDEX` ';
840
841
        $result = $this->execSql($sql);
842
843
        $lastKey = '';
844
        $keyCols = '';
845
        $keyUnique = false;
846
        while ($key = $this->fetch($result)) {
847
            if ($lastKey != $key['INDEX_NAME']) {
848 View Code Duplication
                if (!empty($lastKey)) {
849
                    $tableDef['keys'][$lastKey]['columns'] = $keyCols;
850
                    $tableDef['keys'][$lastKey]['unique'] = $keyUnique;
851
                }
852
                $lastKey = $key['INDEX_NAME'];
853
                $keyCols = $key['COLUMN_NAME'];
854
                if (!empty($key['SUB_PART'])) {
855
                    $keyCols .= ' (' . $key['SUB_PART'] . ')';
856
                }
857
                $keyUnique = !$key['NON_UNIQUE'];
858
            } else {
859
                $keyCols .= ', ' . $key['COLUMN_NAME'];
860
                if (!empty($key['SUB_PART'])) {
861
                    $keyCols .= ' (' . $key['SUB_PART'] . ')';
862
                }
863
            }
864
        };
865 View Code Duplication
        if (!empty($lastKey)) {
866
            $tableDef['keys'][$lastKey]['columns'] = $keyCols;
867
            $tableDef['keys'][$lastKey]['unique'] = $keyUnique;
868
        }
869
870
        return $tableDef;
871
    }
872
873
    /**
874
     * During processing, tables to be created are put in the queue as
875
     * an array('createtable' => tablename) since the definition is not
876
     * complete. This method will expand those references to the full
877
     * ddl to create the table.
878
     *
879
     * @return void
880
     */
881
    protected function expandQueue()
882
    {
883
        foreach ($this->queue as &$ddl) {
884 View Code Duplication
            if (is_array($ddl)) {
885
                if (isset($ddl['createtable'])) {
886
                    $ddl = $this->renderTableCreate($ddl['createtable'], true);
887
                }
888
            }
889
        }
890
    }
891
892
    /**
893
     * Return message from last error encountered
894
     *
895
     * @return string last error message
896
     */
897
    public function getLastError()
898
    {
899
        return $this->lastError;
900
    }
901
902
    /**
903
     * Return code from last error encountered
904
     *
905
     * @return int last error number
906
     */
907
    public function getLastErrNo()
908
    {
909
        return $this->lastErrNo;
910
    }
911
912
    /**
913
     * dumpTables - utility function to dump raw tables array
914
     *
915
     * @return array tables
916
     */
917
    public function dumpTables()
918
    {
919
        return $this->tables;
920
    }
921
922
    /**
923
     * dumpQueue - utility function to dump the work queue
924
     *
925
     * @return array work queue
926
     */
927
    public function dumpQueue()
928
    {
929
        $this->expandQueue();
930
931
        return $this->queue;
932
    }
933
934
    /**
935
     * addToQueue - utility function to add a statement to the work queue
936
     *
937
     * @param string $sql an SQL/DDL statement to add
938
     *
939
     * @return void
940
     */
941
    public function addToQueue($sql)
942
    {
943
        $this->queue[] = $sql;
944
    }
945
946
    /**
947
     * Set lastError as table not established
948
     *
949
     * @return false
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
950
     */
951
    protected function tableNotEstablished()
952
    {
953
        $this->lastError = _DB_XMF_TABLE_IS_NOT_DEFINED;
954
        $this->lastErrNo = -1;
955
        return false;
956
    }
957
}
958