Passed
Pull Request — 4 (#9749)
by Sergey
08:46
created

MySQLSchemaManager::shouldUseIntegerWidth()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 3 Features 2
Metric Value
cc 3
eloc 7
c 4
b 3
f 2
nc 3
nop 0
dl 0
loc 15
rs 10
1
<?php
2
3
namespace SilverStripe\ORM\Connect;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Core\Convert;
7
use LogicException;
8
9
/**
10
 * Represents schema management object for MySQL
11
 */
12
class MySQLSchemaManager extends DBSchemaManager
13
{
14
15
    /**
16
     * Identifier for this schema, used for configuring schema-specific table
17
     * creation options
18
     *
19
     * @skipUpgrade
20
     */
21
    const ID = 'MySQLDatabase';
22
23
    public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null)
24
    {
25
        $fieldSchemas = $indexSchemas = "";
26
27
        if (!empty($options[self::ID])) {
28
            $addOptions = $options[self::ID];
29
        } else {
30
            $addOptions = "ENGINE=InnoDB";
31
        }
32
33
        if (!isset($fields['ID'])) {
34
            $fields['ID'] = "int(11) not null auto_increment";
35
        }
36
        if ($fields) {
37
            foreach ($fields as $k => $v) {
38
                $fieldSchemas .= "\"$k\" $v,\n";
39
            }
40
        }
41
        if ($indexes) {
42
            foreach ($indexes as $k => $v) {
43
                $indexSchemas .= $this->getIndexSqlDefinition($k, $v) . ",\n";
44
            }
45
        }
46
47
        // Switch to "CREATE TEMPORARY TABLE" for temporary tables
48
        $temporary = empty($options['temporary'])
49
                ? ""
50
                : "TEMPORARY";
51
52
        $this->query("CREATE $temporary TABLE \"$table\" (
53
				$fieldSchemas
54
				$indexSchemas
55
				primary key (ID)
56
			) {$addOptions}");
57
58
        return $table;
59
    }
60
61
    public function alterTable(
62
        $tableName,
63
        $newFields = null,
64
        $newIndexes = null,
65
        $alteredFields = null,
66
        $alteredIndexes = null,
67
        $alteredOptions = null,
68
        $advancedOptions = null
69
    ) {
70
        if ($this->isView($tableName)) {
71
            $this->alterationMessage(
72
                sprintf("Table %s not changed as it is a view", $tableName),
73
                "changed"
74
            );
75
            return;
76
        }
77
        $alterList = [];
78
79
        if ($newFields) {
80
            foreach ($newFields as $k => $v) {
81
                $alterList[] .= "ADD \"$k\" $v";
82
            }
83
        }
84
        if ($newIndexes) {
85
            foreach ($newIndexes as $k => $v) {
86
                $alterList[] .= "ADD " . $this->getIndexSqlDefinition($k, $v);
87
            }
88
        }
89
        if ($alteredFields) {
90
            foreach ($alteredFields as $k => $v) {
91
                $alterList[] .= "CHANGE \"$k\" \"$k\" $v";
92
            }
93
        }
94
        if ($alteredIndexes) {
95
            foreach ($alteredIndexes as $k => $v) {
96
                $alterList[] .= "DROP INDEX \"$k\"";
97
                $alterList[] .= "ADD " . $this->getIndexSqlDefinition($k, $v);
98
            }
99
        }
100
101
        $dbID = self::ID;
102
        if ($alteredOptions && isset($alteredOptions[$dbID])) {
103
            $this->query(sprintf("ALTER TABLE \"%s\" %s", $tableName, $alteredOptions[$dbID]));
104
            $this->alterationMessage(
105
                sprintf("Table %s options changed: %s", $tableName, $alteredOptions[$dbID]),
106
                "changed"
107
            );
108
        }
109
110
        $alterations = implode(",\n", $alterList);
111
        $this->query("ALTER TABLE \"$tableName\" $alterations");
112
    }
113
114
    public function isView($tableName)
115
    {
116
        $info = $this->query("SHOW /*!50002 FULL*/ TABLES LIKE '$tableName'")->record();
117
        return $info && strtoupper($info['Table_type']) == 'VIEW';
0 ignored issues
show
Bug Best Practice introduced by
The expression $info of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
118
    }
119
120
    /**
121
     * Renames a table
122
     *
123
     * @param string $oldTableName
124
     * @param string $newTableName
125
     * @throws LogicException
126
     * @return Query
127
     */
128
    public function renameTable($oldTableName, $newTableName)
129
    {
130
        if (!$this->hasTable($oldTableName)) {
131
            throw new LogicException('Table ' . $oldTableName . ' does not exist.');
132
        }
133
134
        return $this->query("ALTER TABLE \"$oldTableName\" RENAME \"$newTableName\"");
135
    }
136
137
    public function checkAndRepairTable($tableName)
138
    {
139
        // Flag to ensure we only send the warning about PDO + native mode once
140
        static $pdo_warning_sent = false;
141
142
        // If running PDO and not in emulated mode, check table will fail
143
        if ($this->database->getConnector() instanceof PDOConnector && !PDOConnector::is_emulate_prepare()) {
144
            if (!$pdo_warning_sent) {
145
                $this->alterationMessage('CHECK TABLE command disabled for PDO in native mode', 'notice');
146
                $pdo_warning_sent = true;
147
            }
148
149
            return true;
150
        }
151
152
        // Perform check
153
        if ($this->runTableCheckCommand("CHECK TABLE \"$tableName\"")) {
154
            return true;
155
        }
156
        $this->alterationMessage(
157
            "Table $tableName: repaired",
158
            "repaired"
159
        );
160
        return $this->runTableCheckCommand("REPAIR TABLE \"$tableName\"");
161
    }
162
163
    /**
164
     * Helper function used by checkAndRepairTable.
165
     * @param string $sql Query to run.
166
     * @return boolean Returns if the query returns a successful result.
167
     */
168
    protected function runTableCheckCommand($sql)
169
    {
170
        $testResults = $this->query($sql);
171
        foreach ($testResults as $testRecord) {
172
            if (strtolower($testRecord['Msg_text']) != 'ok') {
173
                return false;
174
            }
175
        }
176
        return true;
177
    }
178
179
    public function hasTable($table)
180
    {
181
        // MySQLi doesn't like parameterised queries for some queries
182
        // underscores need to be escaped in a SHOW TABLES LIKE query
183
        $sqlTable = str_replace('_', '\\_', $this->database->quoteString($table));
184
        return (bool) ($this->query("SHOW TABLES LIKE $sqlTable")->value());
185
    }
186
187
    public function createField($tableName, $fieldName, $fieldSpec)
188
    {
189
        $this->query("ALTER TABLE \"$tableName\" ADD \"$fieldName\" $fieldSpec");
190
    }
191
192
    public function databaseList()
193
    {
194
        return $this->query("SHOW DATABASES")->column();
195
    }
196
197
    public function databaseExists($name)
198
    {
199
        // MySQLi doesn't like parameterised queries for some queries
200
        $sqlName = addcslashes($this->database->quoteString($name), '%_');
201
        return !!($this->query("SHOW DATABASES LIKE $sqlName")->value());
202
    }
203
204
    public function createDatabase($name)
205
    {
206
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
207
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
208
        $this->query("CREATE DATABASE \"$name\" DEFAULT CHARACTER SET {$charset} DEFAULT COLLATE {$collation}");
209
    }
210
211
    public function dropDatabase($name)
212
    {
213
        $this->query("DROP DATABASE \"$name\"");
214
    }
215
216
    /**
217
     * Change the database type of the given field.
218
     * @param string $tableName The name of the tbale the field is in.
219
     * @param string $fieldName The name of the field to change.
220
     * @param string $fieldSpec The new field specification
221
     */
222
    public function alterField($tableName, $fieldName, $fieldSpec)
223
    {
224
        $this->query("ALTER TABLE \"$tableName\" CHANGE \"$fieldName\" \"$fieldName\" $fieldSpec");
225
    }
226
227
    /**
228
     * Change the database column name of the given field.
229
     *
230
     * @param string $tableName The name of the tbale the field is in.
231
     * @param string $oldName The name of the field to change.
232
     * @param string $newName The new name of the field
233
     */
234
    public function renameField($tableName, $oldName, $newName)
235
    {
236
        $fieldList = $this->fieldList($tableName);
237
        if (array_key_exists($oldName, $fieldList)) {
238
            $this->query("ALTER TABLE \"$tableName\" CHANGE \"$oldName\" \"$newName\" " . $fieldList[$oldName]);
239
        }
240
    }
241
242
    protected static $_cache_collation_info = [];
243
244
    private function shouldUseIntegerWidth()
245
    {
246
        // MySQL 8.0.17 stopped reporting the width attribute for integers
247
        // https://github.com/silverstripe/silverstripe-framework/issues/9453
248
        // Note: MariaDB did not change its behaviour
249
        $forceWidth = Config::inst()->get(self::class, 'schema_use_int_width');
250
        if ($forceWidth !== null) {
251
            return $forceWidth;
252
        }
253
        $v = $this->database->getVersion();
254
        if (false !== strpos($v, 'MariaDB')) {
255
            // MariaDB is included in the version string: https://mariadb.com/kb/en/version/
256
            return true;
257
        }
258
        return version_compare($v, '8.0.17', '<');
259
    }
260
261
    public function fieldList($table)
262
    {
263
        $fields = $this->query("SHOW FULL FIELDS IN \"$table\"");
264
        $fieldList = [];
265
        foreach ($fields as $field) {
266
            $fieldSpec = $field['Type'];
267
            if (!$field['Null'] || $field['Null'] == 'NO') {
268
                $fieldSpec .= ' not null';
269
            }
270
271
            if ($field['Collation'] && $field['Collation'] != 'NULL') {
272
                // Cache collation info to cut down on database traffic
273
                if (!isset(self::$_cache_collation_info[$field['Collation']])) {
274
                    self::$_cache_collation_info[$field['Collation']]
275
                        = $this->query("SHOW COLLATION LIKE '{$field['Collation']}'")->record();
276
                }
277
                $collInfo = self::$_cache_collation_info[$field['Collation']];
278
                $fieldSpec .= " character set $collInfo[Charset] collate $field[Collation]";
279
            }
280
281
            if ($field['Default'] || $field['Default'] === "0" || $field['Default'] === '') {
282
                $fieldSpec .= " default " . $this->database->quoteString($field['Default']);
283
            }
284
            if ($field['Extra']) {
285
                $fieldSpec .= " " . $field['Extra'];
286
            }
287
288
            $fieldList[$field['Field']] = $fieldSpec;
289
        }
290
        return $fieldList;
291
    }
292
293
    /**
294
     * Create an index on a table.
295
     *
296
     * @param string $tableName The name of the table.
297
     * @param string $indexName The name of the index.
298
     * @param string $indexSpec The specification of the index, see {@link SS_Database::requireIndex()} for more
299
     *                          details.
300
     */
301
    public function createIndex($tableName, $indexName, $indexSpec)
302
    {
303
        $this->query("ALTER TABLE \"$tableName\" ADD " . $this->getIndexSqlDefinition($indexName, $indexSpec));
304
    }
305
306
    /**
307
     * Generate SQL suitable for creating this index
308
     *
309
     * @param string $indexName
310
     * @param string|array $indexSpec See {@link requireTable()} for details
311
     * @return string MySQL compatible ALTER TABLE syntax
312
     */
313
    protected function getIndexSqlDefinition($indexName, $indexSpec)
314
    {
315
        if ($indexSpec['type'] == 'using') {
316
            return sprintf('index "%s" using (%s)', $indexName, $this->implodeColumnList($indexSpec['columns']));
317
        } else {
318
            return sprintf('%s "%s" (%s)', $indexSpec['type'], $indexName, $this->implodeColumnList($indexSpec['columns']));
319
        }
320
    }
321
322
    public function alterIndex($tableName, $indexName, $indexSpec)
323
    {
324
        $this->query(sprintf('ALTER TABLE "%s" DROP INDEX "%s"', $tableName, $indexName));
325
        $this->query(sprintf(
326
            'ALTER TABLE "%s" ADD %s "%s" %s',
327
            $tableName,
328
            $indexSpec['type'],
329
            $indexName,
330
            $this->implodeColumnList($indexSpec['columns'])
331
        ));
332
    }
333
334
    protected function indexKey($table, $index, $spec)
335
    {
336
        // MySQL simply uses the same index name as SilverStripe does internally
337
        return $index;
338
    }
339
340
    public function indexList($table)
341
    {
342
        $indexes = $this->query("SHOW INDEXES IN \"$table\"");
343
        $groupedIndexes = [];
344
        $indexList = [];
345
346
        foreach ($indexes as $index) {
347
            $groupedIndexes[$index['Key_name']]['fields'][$index['Seq_in_index']] = $index['Column_name'];
348
349
            if ($index['Index_type'] == 'FULLTEXT') {
350
                $groupedIndexes[$index['Key_name']]['type'] = 'fulltext';
351
            } elseif (!$index['Non_unique']) {
352
                $groupedIndexes[$index['Key_name']]['type'] = 'unique';
353
            } elseif ($index['Index_type'] == 'HASH') {
354
                $groupedIndexes[$index['Key_name']]['type'] = 'hash';
355
            } elseif ($index['Index_type'] == 'RTREE') {
356
                $groupedIndexes[$index['Key_name']]['type'] = 'rtree';
357
            } else {
358
                $groupedIndexes[$index['Key_name']]['type'] = 'index';
359
            }
360
        }
361
362
        if ($groupedIndexes) {
363
            foreach ($groupedIndexes as $index => $details) {
364
                ksort($details['fields']);
365
                $indexList[$index] = [
366
                    'name' => $index,
367
                    'columns' => $details['fields'],
368
                    'type' => $details['type'],
369
                ];
370
            }
371
        }
372
373
        return $indexList;
374
    }
375
376
    public function tableList()
377
    {
378
        $tables = [];
379
        foreach ($this->query("SHOW FULL TABLES WHERE Table_Type != 'VIEW'") as $record) {
380
            $table = reset($record);
381
            $tables[strtolower($table)] = $table;
382
        }
383
        return $tables;
384
    }
385
386
    public function enumValuesForField($tableName, $fieldName)
387
    {
388
        // Get the enum of all page types from the SiteTree table
389
        $classnameinfo = $this->query("DESCRIBE \"$tableName\" \"$fieldName\"")->first();
390
        preg_match_all("/'[^,]+'/", $classnameinfo["Type"], $matches);
391
392
        $classes = [];
393
        foreach ($matches[0] as $value) {
394
            $classes[] = stripslashes(trim($value, "'"));
395
        }
396
        return $classes;
397
    }
398
399
    public function dbDataType($type)
400
    {
401
        $values = [
402
            'unsigned integer' => 'UNSIGNED'
403
        ];
404
405
        if (isset($values[$type])) {
406
            return $values[$type];
407
        } else {
408
            return '';
409
        }
410
    }
411
412
    /**
413
     * Return a boolean type-formatted string
414
     *
415
     * @param array $values Contains a tokenised list of info about this data type
416
     * @return string
417
     */
418
    public function boolean($values)
419
    {
420
        //For reference, this is what typically gets passed to this function:
421
        //$parts=Array('datatype'=>'tinyint', 'precision'=>1, 'sign'=>'unsigned', 'null'=>'not null',
422
        //'default'=>$this->default);
423
        //DB::requireField($this->tableName, $this->name, "tinyint(1) unsigned not null default
424
        //'{$this->defaultVal}'");
425
        $width = $this->shouldUseIntegerWidth() ? '(1)' : '';
426
        return 'tinyint' . $width . ' unsigned not null' . $this->defaultClause($values);
427
    }
428
429
    /**
430
     * Return a date type-formatted string
431
     * For MySQL, we simply return the word 'date', no other parameters are necessary
432
     *
433
     * @param array $values Contains a tokenised list of info about this data type
434
     * @return string
435
     */
436
    public function date($values)
437
    {
438
        //For reference, this is what typically gets passed to this function:
439
        //$parts=Array('datatype'=>'date');
440
        //DB::requireField($this->tableName, $this->name, "date");
441
        return 'date';
442
    }
443
444
    /**
445
     * Return a decimal type-formatted string
446
     *
447
     * @param array $values Contains a tokenised list of info about this data type
448
     * @return string
449
     */
450
    public function decimal($values)
451
    {
452
        //For reference, this is what typically gets passed to this function:
453
        //$parts=Array('datatype'=>'decimal', 'precision'=>"$this->wholeSize,$this->decimalSize");
454
        //DB::requireField($this->tableName, $this->name, "decimal($this->wholeSize,$this->decimalSize)");
455
        // Avoid empty strings being put in the db
456
        if ($values['precision'] == '') {
457
            $precision = 1;
458
        } else {
459
            $precision = $values['precision'];
460
        }
461
462
        // Fix format of default value to match precision
463
        if (isset($values['default']) && is_numeric($values['default'])) {
464
            $decs = strpos($precision, ',') !== false
465
                    ? (int) substr($precision, strpos($precision, ',') + 1)
466
                    : 0;
467
            $values['default'] = number_format($values['default'], $decs, '.', '');
468
        } else {
469
            unset($values['default']);
470
        }
471
472
        return "decimal($precision) not null" . $this->defaultClause($values);
473
    }
474
475
    /**
476
     * Return a enum type-formatted string
477
     *
478
     * @param array $values Contains a tokenised list of info about this data type
479
     * @return string
480
     */
481
    public function enum($values)
482
    {
483
        //For reference, this is what typically gets passed to this function:
484
        //$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=>
485
        // 'utf8_general_ci', 'default'=>$this->default);
486
        //DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set
487
        // utf8 collate utf8_general_ci default '{$this->default}'");
488
        $valuesString = implode(",", Convert::raw2sql($values['enums'], true));
0 ignored issues
show
Bug introduced by
It seems like SilverStripe\Core\Conver...$values['enums'], true) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, 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

488
        $valuesString = implode(",", /** @scrutinizer ignore-type */ Convert::raw2sql($values['enums'], true));
Loading history...
489
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
490
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
491
        return "enum($valuesString) character set {$charset} collate {$collation}" . $this->defaultClause($values);
492
    }
493
494
    /**
495
     * Return a set type-formatted string
496
     *
497
     * @param array $values Contains a tokenised list of info about this data type
498
     * @return string
499
     */
500
    public function set($values)
501
    {
502
        //For reference, this is what typically gets passed to this function:
503
        //$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=>
504
        // 'utf8_general_ci', 'default'=>$this->default);
505
        //DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set
506
        //utf8 collate utf8_general_ci default '{$this->default}'");
507
        $valuesString = implode(",", Convert::raw2sql($values['enums'], true));
0 ignored issues
show
Bug introduced by
It seems like SilverStripe\Core\Conver...$values['enums'], true) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, 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

507
        $valuesString = implode(",", /** @scrutinizer ignore-type */ Convert::raw2sql($values['enums'], true));
Loading history...
508
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
509
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
510
        return "set($valuesString) character set {$charset} collate {$collation}" . $this->defaultClause($values);
511
    }
512
513
    /**
514
     * Return a float type-formatted string
515
     * For MySQL, we simply return the word 'date', no other parameters are necessary
516
     *
517
     * @param array $values Contains a tokenised list of info about this data type
518
     * @return string
519
     */
520
    public function float($values)
521
    {
522
        //For reference, this is what typically gets passed to this function:
523
        //$parts=Array('datatype'=>'float');
524
        //DB::requireField($this->tableName, $this->name, "float");
525
        return "float not null" . $this->defaultClause($values);
526
    }
527
528
    /**
529
     * Return a int type-formatted string
530
     *
531
     * @param array $values Contains a tokenised list of info about this data type
532
     * @return string
533
     */
534
    public function int($values)
535
    {
536
        //For reference, this is what typically gets passed to this function:
537
        //$parts=Array('datatype'=>'int', 'precision'=>11, 'null'=>'not null', 'default'=>(int)$this->default);
538
        //DB::requireField($this->tableName, $this->name, "int(11) not null default '{$this->defaultVal}'");
539
        $width = $this->shouldUseIntegerWidth() ? '(11)' : '';
540
        return 'int' . $width . ' not null' . $this->defaultClause($values);
541
    }
542
543
    /**
544
     * Return a bigint type-formatted string
545
     *
546
     * @param array $values Contains a tokenised list of info about this data type
547
     * @return string
548
     */
549
    public function bigint($values)
550
    {
551
        //For reference, this is what typically gets passed to this function:
552
        //$parts=Array('datatype'=>'bigint', 'precision'=>20, 'null'=>'not null', 'default'=>$this->defaultVal,
553
        //             'arrayValue'=>$this->arrayValue);
554
        //$values=Array('type'=>'bigint', 'parts'=>$parts);
555
        //DB::requireField($this->tableName, $this->name, $values);
556
        $width = $this->shouldUseIntegerWidth() ? '(20)' : '';
557
        return 'bigint' . $width . ' not null' . $this->defaultClause($values);
558
    }
559
560
    /**
561
     * Return a datetime type-formatted string
562
     * For MySQL, we simply return the word 'datetime', no other parameters are necessary
563
     *
564
     * @param array $values Contains a tokenised list of info about this data type
565
     * @return string
566
     */
567
    public function datetime($values)
568
    {
569
        //For reference, this is what typically gets passed to this function:
570
        //$parts=Array('datatype'=>'datetime');
571
        //DB::requireField($this->tableName, $this->name, $values);
572
        return 'datetime';
573
    }
574
575
    /**
576
     * Return a text type-formatted string
577
     *
578
     * @param array $values Contains a tokenised list of info about this data type
579
     * @return string
580
     */
581
    public function text($values)
582
    {
583
        //For reference, this is what typically gets passed to this function:
584
        //$parts=Array('datatype'=>'mediumtext', 'character set'=>'utf8', 'collate'=>'utf8_general_ci');
585
        //DB::requireField($this->tableName, $this->name, "mediumtext character set utf8 collate utf8_general_ci");
586
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
587
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
588
        return 'mediumtext character set ' . $charset . ' collate ' . $collation . $this->defaultClause($values);
589
    }
590
591
    /**
592
     * Return a time type-formatted string
593
     * For MySQL, we simply return the word 'time', no other parameters are necessary
594
     *
595
     * @param array $values Contains a tokenised list of info about this data type
596
     * @return string
597
     */
598
    public function time($values)
599
    {
600
        //For reference, this is what typically gets passed to this function:
601
        //$parts=Array('datatype'=>'time');
602
        //DB::requireField($this->tableName, $this->name, "time");
603
        return 'time';
604
    }
605
606
    /**
607
     * Return a varchar type-formatted string
608
     *
609
     * @param array $values Contains a tokenised list of info about this data type
610
     * @return string
611
     */
612
    public function varchar($values)
613
    {
614
        //For reference, this is what typically gets passed to this function:
615
        //$parts=Array('datatype'=>'varchar', 'precision'=>$this->size, 'character set'=>'utf8', 'collate'=>
616
        //'utf8_general_ci');
617
        //DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate
618
        // utf8_general_ci");
619
        $default = $this->defaultClause($values);
620
        $charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
621
        $collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
622
        return "varchar({$values['precision']}) character set {$charset} collate {$collation}{$default}";
623
    }
624
625
    /*
626
     * Return the MySQL-proprietary 'Year' datatype
627
     *
628
     * @param array $values Contains a tokenised list of info about this data type
629
     * @return string
630
     */
631
    public function year($values)
632
    {
633
        return 'year(4)';
634
    }
635
636
    public function IdColumn($asDbValue = false, $hasAutoIncPK = true)
637
    {
638
        $width = $this->shouldUseIntegerWidth() ? '(11)' : '';
639
        return 'int' . $width . ' not null auto_increment';
640
    }
641
642
    /**
643
     * Parses and escapes the default values for a specification
644
     *
645
     * @param array $values Contains a tokenised list of info about this data type
646
     * @return string Default clause
647
     */
648
    protected function defaultClause($values)
649
    {
650
        if (isset($values['default'])) {
651
            return ' default ' . $this->database->quoteString($values['default']);
652
        }
653
        return '';
654
    }
655
}
656