Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
127 / 127
100.00% covered (success)
100.00%
49 / 49
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractStructure
100.00% covered (success)
100.00%
127 / 127
100.00% covered (success)
100.00%
49 / 49
80
100.00% covered (success)
100.00%
1 / 1
 column
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getColumn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 constraint
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getConstraint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addColumn
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
 hasColumn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addColumnAttribute
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasIncrement
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getIncrement
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 hasPrimary
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getPrimary
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 increment
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 defaultIs
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 nullable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 notNullable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 unsigned
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 index
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 unique
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 primary
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 foreignKey
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 references
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 on
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 onDelete
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 integer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 int
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 bigInt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 mediumInt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 smallInt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 tinyInt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 float
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 real
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 double
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 decimal
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 numeric
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 date
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 time
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 datetime
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 timestamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 year
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 text
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 tinyText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 mediumText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 longText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 blob
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 mediumBlob
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 longBlob
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 char
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 varchar
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getColumnSchema
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Db\Sql\Schema;
16
17/**
18 * Schema abstract design table class for CREATE and ALTER
19 *
20 * @category   Pop
21 * @package    Pop\Db
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    7.0.0
26 */
27abstract class AbstractStructure extends AbstractTable
28{
29
30    /**
31     * Columns to be added or modified
32     * @var array
33     */
34    protected array $columns = [];
35
36    /**
37     * Indices to be created
38     * @var array
39     */
40    protected array $indices = [];
41
42    /**
43     * Constraints to be added
44     * @var array
45     */
46    protected array $constraints = [];
47
48    /**
49     * Current column
50     * @var ?string
51     */
52    protected ?string $currentColumn = null;
53
54    /**
55     * Current constraint
56     * @var ?string
57     */
58    protected ?string $currentConstraint = null;
59
60    /**
61     * Set the current column
62     *
63     * @param  string $column
64     * @return AbstractStructure
65     */
66    public function column(string $column): AbstractStructure
67    {
68        $this->currentColumn = $column;
69        return $this;
70    }
71
72    /**
73     * Get the current column
74     *
75     * @return ?string
76     */
77    public function getColumn(): ?string
78    {
79        return $this->currentColumn;
80    }
81
82    /**
83     * Set the current constraint
84     *
85     * @param  string $constraint
86     * @return AbstractStructure
87     */
88    public function constraint(string $constraint): AbstractStructure
89    {
90        $this->currentConstraint = $constraint;
91        return $this;
92    }
93
94    /**
95     * Get the current constraint
96     *
97     * @return ?string
98     */
99    public function getConstraint(): ?string
100    {
101        return $this->currentConstraint;
102    }
103
104    /**
105     * Add a column
106     *
107     * @param  string $name
108     * @param  string $type
109     * @param  mixed  $size
110     * @param  mixed  $precision
111     * @param  array  $attributes
112     * @return AbstractStructure
113     */
114    public function addColumn(string $name, string $type, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
115    {
116        $this->currentColumn  = $name;
117        $this->columns[$name] = [
118            'type'       => $type,
119            'size'       => $size,
120            'precision'  => $precision,
121            'nullable'   => null,
122            'default'    => null,
123            'increment'  => false,
124            'primary'    => false,
125            'unsigned'   => false,
126            'attributes' => (!empty($attributes)) ? $attributes : [],
127            'after'      => null
128        ];
129
130        return $this;
131    }
132
133    /**
134     * Determine if the table has a column
135     *
136     * @param  string $name
137     * @return bool
138     */
139    public function hasColumn(string $name): bool
140    {
141        return (isset($this->columns[$name]));
142    }
143
144    /**
145     * Add a custom column attribute
146     *
147     * @param  string $attribute
148     * @return AbstractStructure
149     */
150    public function addColumnAttribute(string $attribute): AbstractStructure
151    {
152        if ($this->currentColumn !== null) {
153            $this->columns[$this->currentColumn]['attributes'][] = $attribute;
154        }
155
156        return $this;
157    }
158
159    /**
160     * Determine if the table has an increment column
161     *
162     * @return bool
163     */
164    public function hasIncrement(): bool
165    {
166        $result = false;
167        foreach ($this->columns as $name => $column) {
168            if ($column['increment'] !== false) {
169                $result = true;
170            }
171        }
172        return $result;
173    }
174
175    /**
176     * Get the increment column(s)
177     *
178     * @param  bool $quote
179     * @return array
180     */
181    public function getIncrement(bool $quote = false): array
182    {
183        $result = [];
184        foreach ($this->columns as $name => $column) {
185            if ($column['increment'] !== false) {
186                $result[] = ($quote) ? $this->quoteId($name) : $name;
187            }
188        }
189        return $result;
190    }
191
192    /**
193     * Determine if the table has a primary key column
194     *
195     * @return bool
196     */
197    public function hasPrimary(): bool
198    {
199        $result = false;
200        foreach ($this->columns as $name => $column) {
201            if ($column['primary']) {
202                $result = true;
203            }
204        }
205        return $result;
206    }
207
208    /**
209     * Get the primary key column(s)
210     *
211     * @param  bool $quote
212     * @return array
213     */
214    public function getPrimary(bool$quote = false): array
215    {
216        $result = [];
217        foreach ($this->columns as $name => $column) {
218            if ($column['primary']) {
219                $result[] = ($quote) ? $this->quoteId($name) : $name;
220            }
221        }
222        return $result;
223    }
224
225    /**
226     * Set the current column as an increment column
227     *
228     * @param  int $start
229     * @return AbstractStructure
230     */
231    public function increment(int $start = 1): AbstractStructure
232    {
233        if ($this->currentColumn !== null) {
234            $this->columns[$this->currentColumn]['increment'] = $start;
235        }
236
237        return $this;
238    }
239
240    /**
241     * Set the current column's default value
242     *
243     * @param  mixed $value
244     * @return AbstractStructure
245     */
246    public function defaultIs(mixed $value): AbstractStructure
247    {
248        if ($this->currentColumn !== null) {
249            $this->columns[$this->currentColumn]['default'] = $value;
250            if ($value === null) {
251                $this->columns[$this->currentColumn]['nullable'] = true;
252            }
253        }
254
255        return $this;
256    }
257
258    /**
259     * Set the current column as nullable
260     *
261     * @return AbstractStructure
262     */
263    public function nullable(): AbstractStructure
264    {
265        if ($this->currentColumn !== null) {
266            $this->columns[$this->currentColumn]['nullable'] = true;
267        }
268
269        return $this;
270    }
271
272    /**
273     * Set the current column as NOT nullable
274     *
275     * @return AbstractStructure
276     */
277    public function notNullable(): AbstractStructure
278    {
279        if ($this->currentColumn !== null) {
280            $this->columns[$this->currentColumn]['nullable'] = false;
281        }
282
283        return $this;
284    }
285
286    /**
287     * Set the current column as unsigned
288     *
289     * @return AbstractStructure
290     */
291    public function unsigned(): AbstractStructure
292    {
293        if ($this->currentColumn !== null) {
294            $this->columns[$this->currentColumn]['unsigned'] = true;
295        }
296
297        return $this;
298    }
299
300    /**
301     * Create an index
302     *
303     * @param  string|array $column
304     * @param  ?string $name
305     * @param  string $type
306     * @return AbstractStructure
307     */
308    public function index(string|array $column, ?string $name = null, string $type = 'index'): AbstractStructure
309    {
310        if (!is_array($column)) {
311            $column = [$column];
312        }
313
314        foreach ($column as $c) {
315            if (isset($this->columns[$c]) && ($type == 'primary')) {
316                $this->columns[$c]['primary'] = true;
317            }
318        }
319
320        if ($name === null) {
321            $name = 'index';
322            foreach ($column as $c) {
323                $name .= '_' . strtolower((string)$c);
324            }
325        }
326        $this->indices[$name] = [
327            'column' => $column,
328            'type'   => $type
329        ];
330
331        return $this;
332    }
333
334    /**
335     * Create a UNIQUE index
336     *
337     * @param  string|array|null $column
338     * @param  ?string $name
339     * @return AbstractStructure
340     */
341    public function unique(string|array|null $column = null, ?string $name = null): AbstractStructure
342    {
343        if ($column === null) {
344            $column = $this->currentColumn;
345        }
346        return $this->index($column, $name, 'unique');
347    }
348
349    /**
350     * Create a PRIMARY KEY index
351     *
352     * @param  string|array|null $column
353     * @param  ?string $name
354     * @return AbstractStructure
355     */
356    public function primary(string|array|null $column = null, ?string $name = null): AbstractStructure
357    {
358        if ($column === null) {
359            $column = $this->currentColumn;
360        }
361        return $this->index($column, $name, 'primary');
362    }
363
364    /**
365     * Create a FOREIGN KEY constraint
366     *
367     * @param  string  $column
368     * @param  ?string $name
369     * @return AbstractStructure
370     */
371    public function foreignKey(string $column, ?string $name = null): AbstractStructure
372    {
373        if ($name === null) {
374            $name = 'fk_'. strtolower((string)$column);
375        }
376        $this->currentConstraint  = $name;
377        $this->constraints[$name] = [
378            'column'     => $column,
379            'references' => null,
380            'on'         => null,
381            'delete'     => 'SET NULL'
382        ];
383        return $this;
384    }
385
386    /**
387     * Assign FOREIGN KEY reference table
388     *
389     * @param  string $foreignTable
390     * @return AbstractStructure
391     */
392    public function references(string $foreignTable): AbstractStructure
393    {
394        if ($this->currentConstraint !== null) {
395            $this->constraints[$this->currentConstraint]['references'] = $foreignTable;
396        }
397
398        return $this;
399    }
400
401    /**
402     * Assign FOREIGN KEY reference table column
403     *
404     * @param  string $foreignColumn
405     * @return AbstractStructure
406     */
407    public function on(string $foreignColumn): AbstractStructure
408    {
409        if ($this->currentConstraint !== null) {
410            $this->constraints[$this->currentConstraint]['on'] = $foreignColumn;
411        }
412
413        return $this;
414    }
415
416    /**
417     * Assign FOREIGN KEY ON DELETE action
418     *
419     * @param  ?string $action
420     * @return AbstractStructure
421     */
422    public function onDelete(?string $action = null): AbstractStructure
423    {
424        if ($this->currentConstraint !== null) {
425            $this->constraints[$this->currentConstraint]['delete'] = (strtolower((string)$action) == 'cascade') ?
426                'CASCADE' : 'SET NULL';
427        }
428
429        return $this;
430    }
431
432    /*
433     * INTEGER TYPES
434     */
435
436    /**
437     * Add an INTEGER column
438     *
439     * @param  string $name
440     * @param  mixed  $size
441     * @param  array  $attributes
442     * @return AbstractStructure
443     */
444    public function integer(string $name, mixed $size = null, array $attributes = []): AbstractStructure
445    {
446        return $this->addColumn($name, 'integer', $size, null, $attributes);
447    }
448
449    /**
450     * Add an INT column
451     *
452     * @param  string $name
453     * @param  mixed  $size
454     * @param  array  $attributes
455     * @return AbstractStructure
456     */
457    public function int(string $name, mixed $size = null, array $attributes = []): AbstractStructure
458    {
459        return $this->addColumn($name, 'int', $size, null, $attributes);
460    }
461
462    /**
463     * Add a BIGINT column
464     *
465     * @param  string $name
466     * @param  mixed  $size
467     * @param  array  $attributes
468     * @return AbstractStructure
469     */
470    public function bigInt(string $name, mixed $size = null, array $attributes = []): AbstractStructure
471    {
472        return $this->addColumn($name, 'bigint', $size, null, $attributes);
473    }
474
475    /**
476     * Add a MEDIUMINT column
477     *
478     * @param  string $name
479     * @param  mixed  $size
480     * @param  array  $attributes
481     * @return AbstractStructure
482     */
483    public function mediumInt(string $name, mixed $size = null, array $attributes = []): AbstractStructure
484    {
485        return $this->addColumn($name, 'mediumint', $size, null, $attributes);
486    }
487
488    /**
489     * Add a SMALLINT column
490     *
491     * @param  string $name
492     * @param  mixed  $size
493     * @param  array  $attributes
494     * @return AbstractStructure
495     */
496    public function smallInt(string $name, mixed $size = null, array $attributes = []): AbstractStructure
497    {
498        return $this->addColumn($name, 'smallint', $size, null, $attributes);
499    }
500
501    /**
502     * Add a TINYINT column
503     *
504     * @param  string $name
505     * @param  mixed  $size
506     * @param  array  $attributes
507     * @return AbstractStructure
508     */
509    public function tinyInt(string $name, mixed $size = null, array $attributes = []): AbstractStructure
510    {
511        return $this->addColumn($name, 'tinyint', $size, null, $attributes);
512    }
513
514    /*
515     * NUMERIC TYPES
516     */
517
518    /**
519     * Add a FLOAT column
520     *
521     * @param  string $name
522     * @param  mixed  $size
523     * @param  mixed  $precision
524     * @param  array  $attributes
525     * @return AbstractStructure
526     */
527    public function float(string $name, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
528    {
529        return $this->addColumn($name, 'float', $size, $precision, $attributes);
530    }
531
532    /**
533     * Add a REAL column
534     *
535     * @param  string $name
536     * @param  mixed  $size
537     * @param  mixed  $precision
538     * @param  array  $attributes
539     * @return AbstractStructure
540     */
541    public function real(string $name, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
542    {
543        return $this->addColumn($name, 'real', $size, $precision, $attributes);
544    }
545
546    /**
547     * Add a DOUBLE column
548     *
549     * @param  string $name
550     * @param  mixed  $size
551     * @param  mixed  $precision
552     * @param  array  $attributes
553     * @return AbstractStructure
554     */
555    public function double(string $name, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
556    {
557        return $this->addColumn($name, 'double', $size, $precision, $attributes);
558    }
559
560    /**
561     * Add a DECIMAL column
562     *
563     * @param  string $name
564     * @param  mixed  $size
565     * @param  mixed  $precision
566     * @param  array  $attributes
567     * @return AbstractStructure
568     */
569    public function decimal(string $name, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
570    {
571        return $this->addColumn($name, 'decimal', $size, $precision, $attributes);
572    }
573
574    /**
575     * Add a NUMERIC column
576     *
577     * @param  string $name
578     * @param  mixed  $size
579     * @param  mixed  $precision
580     * @param  array  $attributes
581     * @return AbstractStructure
582     */
583    public function numeric(string $name, mixed $size = null, mixed $precision = null, array $attributes = []): AbstractStructure
584    {
585        return $this->addColumn($name, 'numeric', $size, $precision, $attributes);
586    }
587
588    /*
589     * DATE & TIME TYPES
590     */
591
592    /**
593     * Add a DATE column
594     *
595     * @param  string $name
596     * @param  array  $attributes
597     * @return AbstractStructure
598     */
599    public function date(string $name, array $attributes = []): AbstractStructure
600    {
601        return $this->addColumn($name, 'date', null, null, $attributes);
602    }
603
604    /**
605     * Add a TIME column
606     *
607     * @param  string $name
608     * @param  array  $attributes
609     * @return AbstractStructure
610     */
611    public function time(string $name, array $attributes = []): AbstractStructure
612    {
613        return $this->addColumn($name, 'time', null, null, $attributes);
614    }
615
616    /**
617     * Add a DATETIME column
618     *
619     * @param  string $name
620     * @param  array  $attributes
621     * @return AbstractStructure
622     */
623    public function datetime(string $name, array $attributes = []): AbstractStructure
624    {
625        return $this->addColumn($name, 'datetime', null, null, $attributes);
626    }
627
628    /**
629     * Add a TIMESTAMP column
630     *
631     * @param  string $name
632     * @param  array  $attributes
633     * @return AbstractStructure
634     */
635    public function timestamp(string $name, array $attributes = []): AbstractStructure
636    {
637        return $this->addColumn($name, 'timestamp', null, null, $attributes);
638    }
639
640    /**
641     * Add a YEAR column
642     *
643     * @param  string $name
644     * @param  mixed  $size
645     * @param  array  $attributes
646     * @return AbstractStructure
647     */
648    public function year(string $name, mixed $size = null, array $attributes = []): AbstractStructure
649    {
650        return $this->addColumn($name, 'year', $size, null, $attributes);
651    }
652
653    /*
654     * CHARACTER TYPES
655     */
656
657    /**
658     * Add a TEXT column
659     *
660     * @param  string $name
661     * @param  array  $attributes
662     * @return AbstractStructure
663     */
664    public function text(string $name, array $attributes = []): AbstractStructure
665    {
666        return $this->addColumn($name, 'text', null, null, $attributes);
667    }
668
669    /**
670     * Add a TINYTEXT column
671     *
672     * @param  string $name
673     * @param  array  $attributes
674     * @return AbstractStructure
675     */
676    public function tinyText(string $name, array $attributes = []): AbstractStructure
677    {
678        return $this->addColumn($name, 'tinytext', null, null, $attributes);
679    }
680
681    /**
682     * Add a MEDIUMTEXT column
683     *
684     * @param  string $name
685     * @param  array  $attributes
686     * @return AbstractStructure
687     */
688    public function mediumText(string $name, array $attributes = []): AbstractStructure
689    {
690        return $this->addColumn($name, 'mediumtext', null, null, $attributes);
691    }
692
693    /**
694     * Add a LONGTEXT column
695     *
696     * @param  string $name
697     * @param  array  $attributes
698     * @return AbstractStructure
699     */
700    public function longText(string $name, array $attributes = []): AbstractStructure
701    {
702        return $this->addColumn($name, 'longtext', null, null, $attributes);
703    }
704
705    /**
706     * Add a BLOB column
707     *
708     * @param  string $name
709     * @param  array  $attributes
710     * @return AbstractStructure
711     */
712    public function blob(string $name, array $attributes = []): AbstractStructure
713    {
714        return $this->addColumn($name, 'blob', null, null, $attributes);
715    }
716
717    /**
718     * Add a MEDIUMBLOB column
719     *
720     * @param  string $name
721     * @param  array  $attributes
722     * @return AbstractStructure
723     */
724    public function mediumBlob(string $name, array $attributes = []): AbstractStructure
725    {
726        return $this->addColumn($name, 'mediumblob', null, null, $attributes);
727    }
728
729    /**
730     * Add a LONGBLOB column
731     *
732     * @param  string $name
733     * @param  array  $attributes
734     * @return AbstractStructure
735     */
736    public function longBlob(string $name, array $attributes = []): AbstractStructure
737    {
738        return $this->addColumn($name, 'longblob', null, null, $attributes);
739    }
740
741    /**
742     * Add a CHAR column
743     *
744     * @param  string $name
745     * @param  int    $size
746     * @param  array  $attributes
747     * @return AbstractStructure
748     */
749    public function char(string $name, mixed $size = null, array $attributes = []): AbstractStructure
750    {
751        return $this->addColumn($name, 'char', $size, null, $attributes);
752    }
753
754    /**
755     * Add a VARCHAR column
756     *
757     * @param  string $name
758     * @param  int    $size
759     * @param  array  $attributes
760     * @return AbstractStructure
761     */
762    public function varchar(string $name, mixed $size = null, array $attributes = []): AbstractStructure
763    {
764        return $this->addColumn($name, 'varchar', $size, null, $attributes);
765    }
766
767    /**
768     * Format column schema
769     *
770     * @param  string $name
771     * @param  array $column
772     * @return string
773     */
774    protected function getColumnSchema($name, array $column): string
775    {
776        return Formatter\Column::getColumnSchema($this->getDbType(), $this->quoteId($name), $column, $this->table);
777    }
778
779}