• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aplus-framework / database / 15202200207

21 Apr 2025 08:34PM UTC coverage: 99.02%. Remained the same
15202200207

push

github

natanfelles
Merge branch 'development'

2426 of 2450 relevant lines covered (99.02%)

10.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/Definition/AlterTable.php
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of Aplus Framework Database Library.
4
 *
5
 * (c) Natan Felles <natanfelles@gmail.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Framework\Database\Definition;
11

12
use Framework\Database\Definition\Table\TableDefinition;
13
use Framework\Database\Definition\Table\TableStatement;
14
use InvalidArgumentException;
15
use LogicException;
16

17
/**
18
 * Class AlterTable.
19
 *
20
 * @see https://mariadb.com/kb/en/alter-table/
21
 *
22
 * @package database
23
 */
24
class AlterTable extends TableStatement
25
{
26
    public const string ALGO_COPY = 'COPY';
27
    public const string ALGO_DEFAULT = 'DEFAULT';
28
    public const string ALGO_INPLACE = 'INPLACE';
29
    public const string ALGO_INSTANT = 'INSTANT';
30
    public const string ALGO_NOCOPY = 'NOCOPY';
31
    public const string LOCK_DEFAULT = 'DEFAULT';
32
    public const string LOCK_EXCLUSIVE = 'EXCLUSIVE';
33
    public const string LOCK_NONE = 'NONE';
34
    public const string LOCK_SHARED = 'SHARED';
35

36
    /**
37
     * @return static
38
     */
39
    public function online() : static
40
    {
41
        $this->sql['online'] = true;
1✔
42
        return $this;
1✔
43
    }
44

45
    protected function renderOnline() : ?string
46
    {
47
        if (!isset($this->sql['online'])) {
39✔
48
            return null;
38✔
49
        }
50
        return ' ONLINE';
1✔
51
    }
52

53
    /**
54
     * @return static
55
     */
56
    public function ignore() : static
57
    {
58
        $this->sql['ignore'] = true;
1✔
59
        return $this;
1✔
60
    }
61

62
    protected function renderIgnore() : ?string
63
    {
64
        if (!isset($this->sql['ignore'])) {
39✔
65
            return null;
38✔
66
        }
67
        return ' IGNORE';
1✔
68
    }
69

70
    public function ifExists() : static
71
    {
72
        $this->sql['if_exists'] = true;
1✔
73
        return $this;
1✔
74
    }
75

76
    protected function renderIfExists() : ?string
77
    {
78
        if (!isset($this->sql['if_exists'])) {
39✔
79
            return null;
38✔
80
        }
81
        return ' IF EXISTS';
1✔
82
    }
83

84
    /**
85
     * @param string $tableName
86
     *
87
     * @return static
88
     */
89
    public function table(string $tableName) : static
90
    {
91
        $this->sql['table'] = $tableName;
39✔
92
        return $this;
39✔
93
    }
94

95
    protected function renderTable() : string
96
    {
97
        if (isset($this->sql['table'])) {
39✔
98
            return ' ' . $this->database->protectIdentifier($this->sql['table']);
38✔
99
        }
100
        throw new LogicException('TABLE name must be set');
1✔
101
    }
102

103
    /**
104
     * @param int $seconds
105
     *
106
     * @return static
107
     */
108
    public function wait(int $seconds) : static
109
    {
110
        $this->sql['wait'] = $seconds;
3✔
111
        return $this;
3✔
112
    }
113

114
    protected function renderWait() : ?string
115
    {
116
        if (!isset($this->sql['wait'])) {
38✔
117
            return null;
36✔
118
        }
119
        if ($this->sql['wait'] < 0) {
3✔
120
            throw new InvalidArgumentException(
1✔
121
                "Invalid WAIT value: {$this->sql['wait']}"
1✔
122
            );
1✔
123
        }
124
        return " WAIT {$this->sql['wait']}";
2✔
125
    }
126

127
    public function noWait() : static
128
    {
129
        $this->sql['no_wait'] = true;
1✔
130
        return $this;
1✔
131
    }
132

133
    protected function renderNoWait() : ?string
134
    {
135
        if (!isset($this->sql['no_wait'])) {
37✔
136
            return null;
36✔
137
        }
138
        if (isset($this->sql['wait'])) {
1✔
139
            throw new LogicException('WAIT and NOWAIT can not be used together');
1✔
140
        }
141
        return ' NOWAIT';
1✔
142
    }
143

144
    /**
145
     * @param callable $definition
146
     * @param bool $ifNotExists
147
     *
148
     * @return static
149
     */
150
    public function add(callable $definition, bool $ifNotExists = false) : static
151
    {
152
        $this->sql['add'][] = [
11✔
153
            'definition' => $definition,
11✔
154
            'if_not_exists' => $ifNotExists,
11✔
155
        ];
11✔
156
        return $this;
11✔
157
    }
158

159
    /**
160
     * @param callable $definition
161
     *
162
     * @return static
163
     */
164
    public function addIfNotExists(callable $definition) : static
165
    {
166
        $this->sql['add'][] = [
1✔
167
            'definition' => $definition,
1✔
168
            'if_not_exists' => true,
1✔
169
        ];
1✔
170
        return $this;
1✔
171
    }
172

173
    protected function renderAdd() : ?string
174
    {
175
        if (!isset($this->sql['add'])) {
37✔
176
            return null;
27✔
177
        }
178
        $parts = [];
10✔
179
        foreach ($this->sql['add'] as $add) {
10✔
180
            $definition = new TableDefinition(
10✔
181
                $this->database,
10✔
182
                $add['if_not_exists'] ? 'IF NOT EXISTS' : null
10✔
183
            );
10✔
184
            $add['definition']($definition);
10✔
185
            $part = $definition->sql('ADD');
10✔
186
            if ($part) {
10✔
187
                $parts[] = $part;
9✔
188
            }
189
        }
190
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
10✔
191
    }
192

193
    /**
194
     * @param callable $definition
195
     * @param bool $ifExists
196
     *
197
     * @return static
198
     */
199
    public function change(callable $definition, bool $ifExists = false) : static
200
    {
201
        $this->sql['change'][] = [
3✔
202
            'definition' => $definition,
3✔
203
            'if_exists' => $ifExists,
3✔
204
        ];
3✔
205
        return $this;
3✔
206
    }
207

208
    public function changeIfExists(callable $definition) : static
209
    {
210
        $this->sql['change'][] = [
1✔
211
            'definition' => $definition,
1✔
212
            'if_exists' => true,
1✔
213
        ];
1✔
214
        return $this;
1✔
215
    }
216

217
    protected function renderChange() : ?string
218
    {
219
        if (!isset($this->sql['change'])) {
37✔
220
            return null;
34✔
221
        }
222
        $parts = [];
3✔
223
        foreach ($this->sql['change'] as $change) {
3✔
224
            $definition = new TableDefinition(
3✔
225
                $this->database,
3✔
226
                $change['if_exists'] ? 'IF EXISTS' : null
3✔
227
            );
3✔
228
            $change['definition']($definition);
3✔
229
            $part = $definition->sql('CHANGE');
3✔
230
            if ($part) {
3✔
231
                $parts[] = $part;
2✔
232
            }
233
        }
234
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
3✔
235
    }
236

237
    /**
238
     * @param callable $definition
239
     * @param bool $ifExists
240
     *
241
     * @return static
242
     */
243
    public function modify(callable $definition, bool $ifExists = false) : static
244
    {
245
        $this->sql['modify'][] = [
3✔
246
            'definition' => $definition,
3✔
247
            'if_exists' => $ifExists,
3✔
248
        ];
3✔
249
        return $this;
3✔
250
    }
251

252
    public function modifyIfExists(callable $definition) : static
253
    {
254
        $this->sql['modify'][] = [
1✔
255
            'definition' => $definition,
1✔
256
            'if_exists' => true,
1✔
257
        ];
1✔
258
        return $this;
1✔
259
    }
260

261
    protected function renderModify() : ?string
262
    {
263
        if (!isset($this->sql['modify'])) {
37✔
264
            return null;
34✔
265
        }
266
        $parts = [];
3✔
267
        foreach ($this->sql['modify'] as $modify) {
3✔
268
            $definition = new TableDefinition(
3✔
269
                $this->database,
3✔
270
                $modify['if_exists'] ? 'IF EXISTS' : null
3✔
271
            );
3✔
272
            $modify['definition']($definition);
3✔
273
            $part = $definition->sql('MODIFY');
3✔
274
            if ($part) {
3✔
275
                $parts[] = $part;
2✔
276
            }
277
        }
278
        return $parts ? \implode(',' . \PHP_EOL, $parts) : null;
3✔
279
    }
280

281
    public function dropColumn(string $name, bool $ifExists = false) : static
282
    {
283
        $this->sql['drop_columns'][$name] = $ifExists;
2✔
284
        return $this;
2✔
285
    }
286

287
    public function dropColumnIfExists(string $name) : static
288
    {
289
        $this->sql['drop_columns'][$name] = true;
1✔
290
        return $this;
1✔
291
    }
292

293
    protected function renderDropColumns() : ?string
294
    {
295
        if (!isset($this->sql['drop_columns'])) {
37✔
296
            return null;
34✔
297
        }
298
        $drops = [];
3✔
299
        foreach ($this->sql['drop_columns'] as $name => $ifExists) {
3✔
300
            $name = $this->database->protectIdentifier($name);
3✔
301
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
3✔
302
            $drops[] = ' DROP COLUMN ' . $ifExists . $name;
3✔
303
        }
304
        return \implode(',' . \PHP_EOL, $drops);
3✔
305
    }
306

307
    public function dropPrimaryKey() : static
308
    {
309
        $this->sql['drop_primary_key'] = true;
1✔
310
        return $this;
1✔
311
    }
312

313
    protected function renderDropPrimaryKey() : ?string
314
    {
315
        if (!isset($this->sql['drop_primary_key'])) {
37✔
316
            return null;
36✔
317
        }
318
        return ' DROP PRIMARY KEY';
1✔
319
    }
320

321
    public function dropKey(string $name, bool $ifExists = false) : static
322
    {
323
        $this->sql['drop_keys'][$name] = $ifExists;
1✔
324
        return $this;
1✔
325
    }
326

327
    public function dropKeyIfExists(string $name) : static
328
    {
329
        $this->sql['drop_keys'][$name] = true;
1✔
330
        return $this;
1✔
331
    }
332

333
    protected function renderDropKeys() : ?string
334
    {
335
        if (!isset($this->sql['drop_keys'])) {
37✔
336
            return null;
35✔
337
        }
338
        $drops = [];
2✔
339
        foreach ($this->sql['drop_keys'] as $name => $ifExists) {
2✔
340
            $name = $this->database->protectIdentifier($name);
2✔
341
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
2✔
342
            $drops[] = ' DROP KEY ' . $ifExists . $name;
2✔
343
        }
344
        return \implode(',' . \PHP_EOL, $drops);
2✔
345
    }
346

347
    public function dropForeignKey(string $name, bool $ifExists = false) : static
348
    {
349
        $this->sql['drop_foreign_keys'][$name] = $ifExists;
1✔
350
        return $this;
1✔
351
    }
352

353
    public function dropForeignKeyIfExists(string $name) : static
354
    {
355
        $this->sql['drop_foreign_keys'][$name] = true;
1✔
356
        return $this;
1✔
357
    }
358

359
    protected function renderDropForeignKeys() : ?string
360
    {
361
        if (!isset($this->sql['drop_foreign_keys'])) {
37✔
362
            return null;
35✔
363
        }
364
        $drops = [];
2✔
365
        foreach ($this->sql['drop_foreign_keys'] as $name => $ifExists) {
2✔
366
            $name = $this->database->protectIdentifier($name);
2✔
367
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
2✔
368
            $drops[] = ' DROP FOREIGN KEY ' . $ifExists . $name;
2✔
369
        }
370
        return \implode(',' . \PHP_EOL, $drops);
2✔
371
    }
372

373
    public function dropConstraint(string $name, bool $ifExists = false) : static
374
    {
375
        $this->sql['drop_constraints'][$name] = $ifExists;
1✔
376
        return $this;
1✔
377
    }
378

379
    public function dropConstraintIfExists(string $name) : static
380
    {
381
        $this->sql['drop_constraints'][$name] = true;
1✔
382
        return $this;
1✔
383
    }
384

385
    protected function renderDropConstraints() : ?string
386
    {
387
        if (!isset($this->sql['drop_constraints'])) {
37✔
388
            return null;
35✔
389
        }
390
        $drops = [];
2✔
391
        foreach ($this->sql['drop_constraints'] as $name => $ifExists) {
2✔
392
            $name = $this->database->protectIdentifier($name);
2✔
393
            $ifExists = $ifExists ? 'IF EXISTS ' : '';
2✔
394
            $drops[] = ' DROP CONSTRAINT ' . $ifExists . $name;
2✔
395
        }
396
        return \implode(',' . \PHP_EOL, $drops);
2✔
397
    }
398

399
    public function disableKeys() : static
400
    {
401
        $this->sql['disable_keys'] = true;
1✔
402
        return $this;
1✔
403
    }
404

405
    protected function renderDisableKeys() : ?string
406
    {
407
        if (!isset($this->sql['disable_keys'])) {
37✔
408
            return null;
36✔
409
        }
410
        return ' DISABLE KEYS';
1✔
411
    }
412

413
    public function enableKeys() : static
414
    {
415
        $this->sql['enable_keys'] = true;
1✔
416
        return $this;
1✔
417
    }
418

419
    protected function renderEnableKeys() : ?string
420
    {
421
        if (!isset($this->sql['enable_keys'])) {
37✔
422
            return null;
36✔
423
        }
424
        return ' ENABLE KEYS';
1✔
425
    }
426

427
    public function renameTo(string $newTableName) : static
428
    {
429
        $this->sql['rename_to'] = $newTableName;
1✔
430
        return $this;
1✔
431
    }
432

433
    protected function renderRenameTo() : ?string
434
    {
435
        if (!isset($this->sql['rename_to'])) {
37✔
436
            return null;
36✔
437
        }
438
        return ' RENAME TO ' . $this->database->protectIdentifier($this->sql['rename_to']);
1✔
439
    }
440

441
    public function orderBy(string $column, string ...$columns) : static
442
    {
443
        foreach ([$column, ...$columns] as $col) {
1✔
444
            $this->sql['order_by'][] = $col;
1✔
445
        }
446
        return $this;
1✔
447
    }
448

449
    protected function renderOrderBy() : ?string
450
    {
451
        if (!isset($this->sql['order_by'])) {
37✔
452
            return null;
36✔
453
        }
454
        $columns = [];
1✔
455
        foreach ($this->sql['order_by'] as $column) {
1✔
456
            $columns[] = $this->database->protectIdentifier($column);
1✔
457
        }
458
        return ' ORDER BY ' . \implode(', ', $columns);
1✔
459
    }
460

461
    public function renameColumn(string $name, string $newName) : static
462
    {
463
        $this->sql['rename_columns'][$name] = $newName;
2✔
464
        return $this;
2✔
465
    }
466

467
    protected function renderRenameColumns() : ?string
468
    {
469
        if (!isset($this->sql['rename_columns'])) {
37✔
470
            return null;
35✔
471
        }
472
        $renames = [];
2✔
473
        foreach ($this->sql['rename_columns'] as $name => $newName) {
2✔
474
            $name = $this->database->protectIdentifier($name);
2✔
475
            $newName = $this->database->protectIdentifier($newName);
2✔
476
            $renames[] = ' RENAME COLUMN ' . $name . ' TO ' . $newName;
2✔
477
        }
478
        return \implode(',' . \PHP_EOL, $renames);
2✔
479
    }
480

481
    public function renameKey(string $name, string $newName) : static
482
    {
483
        $this->sql['rename_keys'][$name] = $newName;
1✔
484
        return $this;
1✔
485
    }
486

487
    protected function renderRenameKeys() : ?string
488
    {
489
        if (!isset($this->sql['rename_keys'])) {
37✔
490
            return null;
36✔
491
        }
492
        $renames = [];
1✔
493
        foreach ($this->sql['rename_keys'] as $name => $newName) {
1✔
494
            $name = $this->database->protectIdentifier($name);
1✔
495
            $newName = $this->database->protectIdentifier($newName);
1✔
496
            $renames[] = ' RENAME KEY ' . $name . ' TO ' . $newName;
1✔
497
        }
498
        return \implode(',' . \PHP_EOL, $renames);
1✔
499
    }
500

501
    public function convertToCharset(string $charset, ?string $collation = null) : static
502
    {
503
        $this->sql['convert_to_charset'] = [
1✔
504
            'charset' => $charset,
1✔
505
            'collation' => $collation,
1✔
506
        ];
1✔
507
        return $this;
1✔
508
    }
509

510
    protected function renderConvertToCharset() : ?string
511
    {
512
        if (!isset($this->sql['convert_to_charset'])) {
37✔
513
            return null;
36✔
514
        }
515
        $charset = $this->database->quote($this->sql['convert_to_charset']['charset']);
1✔
516
        $convert = ' CONVERT TO CHARACTER SET ' . $charset;
1✔
517
        if (isset($this->sql['convert_to_charset']['collation'])) {
1✔
518
            $convert .= ' COLLATE ' . $this->database->quote($this->sql['convert_to_charset']['collation']);
1✔
519
        }
520
        return $convert;
1✔
521
    }
522

523
    public function charset(?string $charset) : static
524
    {
525
        $this->sql['charset'] = $charset ?? 'DEFAULT';
1✔
526
        return $this;
1✔
527
    }
528

529
    protected function renderCharset() : ?string
530
    {
531
        if (!isset($this->sql['charset'])) {
37✔
532
            return null;
36✔
533
        }
534
        $charset = \strtolower($this->sql['charset']);
1✔
535
        if ($charset === 'default') {
1✔
536
            return ' DEFAULT CHARACTER SET';
1✔
537
        }
538
        return ' CHARACTER SET = ' . $this->database->quote($charset);
1✔
539
    }
540

541
    public function collate(?string $collation) : static
542
    {
543
        $this->sql['collate'] = $collation ?? 'DEFAULT';
1✔
544
        return $this;
1✔
545
    }
546

547
    protected function renderCollate() : ?string
548
    {
549
        if (!isset($this->sql['collate'])) {
37✔
550
            return null;
36✔
551
        }
552
        $collate = \strtolower($this->sql['collate']);
1✔
553
        if ($collate === 'default') {
1✔
554
            return ' DEFAULT COLLATE';
1✔
555
        }
556
        return ' COLLATE = ' . $this->database->quote($collate);
1✔
557
    }
558

559
    /**
560
     * @param string $type
561
     *
562
     * @see https://mariadb.com/kb/en/alter-table/#lock
563
     * @see AlterTable::LOCK_DEFAULT
564
     * @see AlterTable::LOCK_EXCLUSIVE
565
     * @see AlterTable::LOCK_NONE
566
     * @see AlterTable::LOCK_SHARED
567
     *
568
     * @return static
569
     */
570
    public function lock(string $type) : static
571
    {
572
        $this->sql['lock'] = $type;
1✔
573
        return $this;
1✔
574
    }
575

576
    protected function renderLock() : ?string
577
    {
578
        if (!isset($this->sql['lock'])) {
37✔
579
            return null;
36✔
580
        }
581
        $lock = \strtoupper($this->sql['lock']);
1✔
582
        if (!\in_array($lock, [
1✔
583
            static::LOCK_DEFAULT,
1✔
584
            static::LOCK_EXCLUSIVE,
1✔
585
            static::LOCK_NONE,
1✔
586
            static::LOCK_SHARED,
1✔
587
        ], true)) {
1✔
588
            throw new InvalidArgumentException("Invalid LOCK value: {$this->sql['lock']}");
1✔
589
        }
590
        return ' LOCK = ' . $lock;
1✔
591
    }
592

593
    public function force() : static
594
    {
595
        $this->sql['force'] = true;
1✔
596
        return $this;
1✔
597
    }
598

599
    protected function renderForce() : ?string
600
    {
601
        if (!isset($this->sql['force'])) {
37✔
602
            return null;
36✔
603
        }
604
        return ' FORCE';
1✔
605
    }
606

607
    /**
608
     * @param string $algo
609
     *
610
     * @see https://mariadb.com/kb/en/innodb-online-ddl-overview/#algorithm
611
     * @see AlterTable::ALGO_COPY
612
     * @see AlterTable::ALGO_DEFAULT
613
     * @see AlterTable::ALGO_INPLACE
614
     * @see AlterTable::ALGO_INSTANT
615
     * @see AlterTable::ALGO_NOCOPY
616
     *
617
     * @return static
618
     */
619
    public function algorithm(string $algo) : static
620
    {
621
        $this->sql['algorithm'] = $algo;
1✔
622
        return $this;
1✔
623
    }
624

625
    protected function renderAlgorithm() : ?string
626
    {
627
        if (!isset($this->sql['algorithm'])) {
37✔
628
            return null;
36✔
629
        }
630
        $algo = \strtoupper($this->sql['algorithm']);
1✔
631
        if (!\in_array($algo, [
1✔
632
            static::ALGO_COPY,
1✔
633
            static::ALGO_DEFAULT,
1✔
634
            static::ALGO_INPLACE,
1✔
635
            static::ALGO_INSTANT,
1✔
636
            static::ALGO_NOCOPY,
1✔
637
        ], true)) {
1✔
638
            throw new InvalidArgumentException("Invalid ALGORITHM value: {$this->sql['algorithm']}");
1✔
639
        }
640
        return ' ALGORITHM = ' . $algo;
1✔
641
    }
642

643
    public function sql() : string
644
    {
645
        $sql = 'ALTER' . $this->renderOnline() . $this->renderIgnore();
39✔
646
        $sql .= ' TABLE' . $this->renderIfExists();
39✔
647
        $sql .= $this->renderTable() . \PHP_EOL;
39✔
648
        $part = $this->renderWait() . $this->renderNoWait();
38✔
649
        if ($part) {
37✔
650
            $sql .= $part . \PHP_EOL;
2✔
651
        }
652
        $sql .= $this->joinParts([
37✔
653
            $this->renderOptions(),
37✔
654
            $this->renderAdd(),
37✔
655
            $this->renderChange(),
37✔
656
            $this->renderModify(),
37✔
657
            $this->renderDropColumns(),
37✔
658
            $this->renderDropPrimaryKey(),
37✔
659
            $this->renderDropKeys(),
37✔
660
            $this->renderDropForeignKeys(),
37✔
661
            $this->renderDropConstraints(),
37✔
662
            $this->renderDisableKeys(),
37✔
663
            $this->renderEnableKeys(),
37✔
664
            $this->renderRenameTo(),
37✔
665
            $this->renderOrderBy(),
37✔
666
            $this->renderRenameColumns(),
37✔
667
            $this->renderRenameKeys(),
37✔
668
            $this->renderConvertToCharset(),
37✔
669
            $this->renderCharset(),
37✔
670
            $this->renderCollate(),
37✔
671
            $this->renderAlgorithm(),
37✔
672
            $this->renderLock(),
37✔
673
            $this->renderForce(),
37✔
674
        ]);
37✔
675
        return $sql;
37✔
676
    }
677

678
    /**
679
     * @param array<string|null> $parts
680
     *
681
     * @return string
682
     */
683
    protected function joinParts(array $parts) : string
684
    {
685
        $result = '';
37✔
686
        $hasBefore = false;
37✔
687
        foreach ($parts as $part) {
37✔
688
            if ($part !== null) {
37✔
689
                $result .= $hasBefore ? ',' . \PHP_EOL : '';
34✔
690
                $result .= $part;
34✔
691
                $hasBefore = true;
34✔
692
            }
693
        }
694
        return $result;
37✔
695
    }
696

697
    /**
698
     * Runs the ALTER TABLE statement.
699
     *
700
     * @return int|string The number of affected rows
701
     */
702
    public function run() : int | string
703
    {
704
        return $this->database->exec($this->sql());
1✔
705
    }
706
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc