yf_db_ddl_parser_mysql class

Fileplugins/db/classes/db/yf_db_ddl_parser_mysql.class.php
Use_class('db_ddl_parser_mysql', 'classes/db/')

Public properties

A site sets them in its config with $PROJECT_CONF['db_ddl_parser_mysql']['NAME'] = ...;

PropertyDefaultDescription
$RAW_IN_RESULTS false

Public methods 9

create

public function create(array $data, $params = [])
  • $data array
    public function create(array $data, $params = [])
    {
        if ( ! strlen($data['name']) || empty($data['fields'])) {
            return false;
        }
        $table_name = $data['name'];
        $lines = [];
        foreach ((array) $data['fields'] as $name => $v) {
            $lines[] = $this->create_column_line($name, $v);
        }
        foreach ((array) $data['indexes'] as $name => $v) {
            $lines[] = $this->create_index_line($name, $v);
        }
        foreach ((array) ($data['foreign_keys'] ?? []) as $name => $v) {
            $lines[] = $this->create_fk_line($name, $v);
        }
        $options = [];
        foreach ((array) ($data['options'] ?? []) as $k => $v) {
            if ($k == 'charset') {
                $k = 'DEFAULT CHARSET';
            }
            $options[$k] = strtoupper($k) . '=' . $v;
        }
        return 'CREATE TABLE ' . $this->escape_table_name($table_name) . ' (' . PHP_EOL . implode(',' . PHP_EOL, $lines) . PHP_EOL . ')' . ($options ? ' ' . implode(' ', $options) : '') . ';';
    }
public function create_column_line($name = '', $v = [], $params = [])

Useful for ALTER TABLE.

    public function create_column_line($name = '', $v = [], $params = [])
    {
        if (is_array($name)) {
            $v = $name;
            $name = $v['name'];
        }
        $name = strtolower($name);
        $v['type'] = strtolower($v['type']);
        if (strpos($v['type'], 'int') !== false && ! ($v['length'] ?? null)) {
            $v['length'] = $this->_get_int_def_length($v['type']);
        }
        $type_braces = (isset($v['length']) && is_numeric($v['length']) ? '(' . $v['length'] . (isset($v['decimals']) && is_numeric($v['decimals']) ? ',' . $v['decimals'] : '') . ')' : '');
        if (in_array($v['type'], ['enum', 'set']) && is_array($v['values'] ?? null) && count((array) $v['values'])) {
            $type_braces = '(\'' . implode('\',\'', $v['values']) . '\')';
        }
        $def = false;
        if (($v['default'] ?? null) === 'NULL') {
            $def = 'NULL';
        } elseif ($v['type'] == 'timestamp') {
            $def = $v['default'] ?? null;
        } elseif (($v['default'] ?? null) !== null) {
            $def = '\'' . $v['default'] . '\'';
        }
        return $this->_implode_line([
            'name' => $this->escape_key($name),
            'type' => $v['type'] . $type_braces,
            'unsigned' => ($v['unsigned'] ?? null) ? 'unsigned' : '',
            'charset' => ($v['charset'] ?? null) && ($v['collate'] ?? null) ? 'CHARACTER SET ' . strtolower($v['charset']) : '',
            'collate' => ($v['collate'] ?? null) ? 'COLLATE ' . strtolower($v['collate']) : '',
            'nullable' => ! ($v['nullable'] ?? null) ? 'NOT NULL' : '',
            'default' => $def ? 'DEFAULT ' . $def : '',
            'auto_inc' => ($v['auto_inc'] ?? null) ? 'AUTO_INCREMENT' : '',
            'on_update' => ($v['on_update'] ?? null) ?: '',
        ]);
    }
public function create_fk_line($name = '', $v = [], $params = [])

Useful for ALTER FOREIGN KEY.

    public function create_fk_line($name = '', $v = [], $params = [])
    {
        if (is_array($name)) {
            $v = $name;
            $name = $v['name'];
        }
        return $this->_implode_line([
            'begin' => 'CONSTRAINT',
            'name' => $this->escape_key(strtolower($name)),
            'fk' => 'FOREIGN KEY',
            'columns' => strtolower('(' . implode(',', $this->escape_key($v['columns'])) . ')'),
            'ref' => 'REFERENCES',
            'ref_table' => '`' . $v['ref_table'] . '`',
            'ref_columns' => strtolower('(' . implode(',', $this->escape_key($v['ref_columns'])) . ')'),
            'on_delete' => $v['on_delete'] ? 'ON DELETE ' . strtoupper(str_replace('_', ' ', $v['on_delete'])) : '',
            'on_update' => $v['on_update'] ? 'ON UPDATE ' . strtoupper(str_replace('_', ' ', $v['on_update'])) : '',
        ]);
    }
public function create_index_line($name = '', $v = [], $params = [])

Useful for ALTER INDEX.

    public function create_index_line($name = '', $v = [], $params = [])
    {
        if (is_array($name)) {
            $v = $name;
            $name = $v['name'];
        }
        $type = 'KEY';
        $v['type'] = strtolower($v['type']);
        if ($v['type'] == 'primary') {
            $type = 'PRIMARY KEY';
            $name = 'PRIMARY';
        } elseif ($v['type'] == 'unique') {
            $type = 'UNIQUE KEY';
        } elseif ($v['type'] == 'fulltext') {
            $type = 'FULLTEXT KEY';
        } elseif ($v['type'] == 'spatial') {
            $type = 'SPATIAL KEY';
        }
        if ($name != 'PRIMARY') {
            $name = strtolower($name);
        }
        return $this->_implode_line([
            'type' => $type,
            'name' => strlen($name) && ! is_numeric($name) && in_array($v['type'], ['index', 'unique', 'fulltext', 'spatial']) ? $this->escape_key($name) : '',
            'columns' => strtolower('(' . implode(',', $this->escape_key($v['columns'])) . ')'),
        ]);
    }

escape_key

public function escape_key($data)
    public function escape_key($data)
    {
        if (is_array($data)) {
            $func = __FUNCTION__;
            foreach ((array) $data as $k => $v) {
                $data[$k] = $this->$func($v);
            }
            return $data;
        }
        return '`' . $data . '`';
    }
public function escape_table_name($name = '')
    public function escape_table_name($name = '')
    {
        $name = trim($name);
        if ( ! strlen($name)) {
            return false;
        }
        $db = '';
        $table = '';
        if (strpos($name, '.') !== false) {
            list($db, $table) = explode('.', $name);
            $db = trim($db);
            $table = trim($table);
        } else {
            $table = $name;
        }
        if ( ! strlen($table)) {
            return false;
        }
        return (strlen($db) ? $this->escape_key($db) . '.' : '') . $this->escape_key($table);
    }

parse

public function parse($sql)
    public function parse($sql)
    {
        $parsed = $this->parser->parse($sql);

        $table_name = ($parsed['TABLE']['no_quotes']['parts'][0] ?? null) ?: '';
        $tmp_create_def = ($parsed['TABLE']['create-def']['sub_tree'] ?? null) ?: [];
        $tmp_options = ($parsed['TABLE']['options'] ?? null) ?: [];

        $struct = [
            'name' => $table_name,
            'fields' => [],
            'indexes' => [],
            'foreign_keys' => [],
            'options' => [],
        ];

        foreach ((array) $tmp_create_def as $v) {
            if ($v['expr_type'] == 'column-def') {
                $name = null;
                $type = null;
                $length = null;
                $unsigned = null;
                $nullable = false;
                $charset = null;
                $collate = null;
                $default = null;
                $auto_inc = null;
                $primary = false;
                $unique = false;
                $decimals = null;
                $values = null; // ENUM and SET
                $on_update = null; // TIMESTAMP and DATETIME
                foreach ((array) $v['sub_tree'] as $v2) {
                    if ($v2['expr_type'] == 'colref') {
                        $name = $v2['no_quotes']['parts'][0];
                    } elseif ($v2['expr_type'] == 'column-type') {
                        foreach ((array) $v2['sub_tree'] as $k3 => $v3) {
                            if (isset($v3['unsigned'])) {
                                $unsigned = $v3['unsigned'];
                            }
                            if ($v3['expr_type'] == 'data-type') {
                                $type = $v3['base_expr'];
                                $length = $v3['length'] ?? null;
                                $decimals = $v3['decimals'] ?? null;
                            } elseif ($v3['expr_type'] == 'default-value') {
                                $default = trim($v3['base_expr'], '"\'');
                            } elseif ($v3['expr_type'] == 'reserved' && in_array($v3['base_expr'], ['enum', 'set'])) {
                                $type = $v3['base_expr'];
                                if ($v3['sub_tree']['expr_type'] != 'bracket_expression') {
                                    continue;
                                }
                                $values = [];
                                foreach ((array) $v3['sub_tree']['sub_tree'] as $v4) {
                                    if ($v4['expr_type'] == 'const') {
                                        $_val = trim($v4['base_expr'], '"\'');
                                        $values[$_val] = $_val;
                                    }
                                }
                            } elseif ($v3['expr_type'] == 'reserved' && strtoupper($v3['base_expr']) === 'DEFAULT') {
                                $next1 = $v2['sub_tree'][$k3 + 1];
                                $next2 = $v2['sub_tree'][$k3 + 2];
                                if ($next1['expr_type'] == 'reserved') {
                                    $default = (string) ($next1['base_expr']);
                                    if ($next2['expr_type'] == 'reserved') {
                                        $default .= ' ' . (string) ($next2['base_expr']);
                                    }
                                }
                            }
                        }
                        // http://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html
                        // As of MySQL 5.6.5, TIMESTAMP and DATETIME columns can be automatically initializated and updated to the current date and time (that is, the current timestamp).
                        // Before 5.6.5, this is true only for TIMESTAMP, and for at most one TIMESTAMP column per table
                        if (in_array($type, ['timestamp', 'datetime'])) {
                            $try = 'ON UPDATE CURRENT_TIMESTAMP';
                            if (strpos($v2['base_expr'], $try) !== false) {
                                $on_update = $try;
                            }
                        }
                        $nullable = $v2['nullable'] ?? null;
                        $auto_inc = $v2['auto_inc'] ?? null;
                        $primary = $v2['primary'] ?? null;
                        $unique = $v2['unique'] ?? null;
                        $charset = $v2['charset'] ?? null;
                        $collate = $v2['collate'] ?? null;
                    }
                }
                if ($auto_inc) {
                    $primary = true;
                }
                $name = strtolower($name);
                $type = strtolower($type);
                if (strpos($type, 'int') !== false && ! $length) {
                    $length = $this->_get_int_def_length($type);
                }
                $struct['fields'][$name] = [
                    'name' => $name,
                    'type' => $type,
                    'length' => $length ? (int) $length : null,
                    'decimals' => $decimals,
                    'unsigned' => $unsigned,
                    'nullable' => $nullable,
                    'default' => $default,
                    'charset' => $charset ? strtolower($charset) : null,
                    'collate' => $collate ? strtolower($collate) : null,
                    'auto_inc' => $auto_inc,
                    'primary' => $primary,
                    'unique' => $unique,
                    'values' => ! empty($values) ? $values : null,
                ];
                if ($on_update) {
                    $struct['fields'][$name]['on_update'] = $on_update;
                }
                if ($this->RAW_IN_RESULTS) {
                    $struct['fields'][$name]['raw'] = $v['base_expr'];
                }
            } elseif ($v['expr_type'] == 'primary-key') {
                $name = 'PRIMARY';
                $type = 'primary';
                $columns = [];
                foreach ((array) $v['sub_tree'] as $v2) {
                    if ($v2['expr_type'] == 'column-list') {
                        foreach ((array) $v2['sub_tree'] as $v3) {
                            if ($v3['expr_type'] == 'index-column') {
                                $index_col_name = $v3['no_quotes']['parts'][0];
                                $columns[$index_col_name] = $index_col_name;
                            }
                        }
                    }
                }
                $struct['indexes'][$name] = [
                    'name' => $name,
                    'type' => strtolower($type),
                    'columns' => $columns,
                ];
                if ($this->RAW_IN_RESULTS) {
                    $struct['indexes'][$name]['raw'] = $v['base_expr'];
                }
            } elseif ($v['expr_type'] == 'index' || $v['expr_type'] == 'fulltext-index' || $v['expr_type'] == 'spatial-index') {
                $name = null;
                $type = 'index';
                $base = strtoupper(trim($v['base_expr']));
                if (substr($base, 0, strlen('UNIQUE')) == 'UNIQUE') {
                    $type = 'unique';
                } elseif (substr($base, 0, strlen('FULLTEXT')) == 'FULLTEXT' || $v['expr_type'] == 'fulltext-index') {
                    $type = 'fulltext';
                } elseif (substr($base, 0, strlen('SPATIAL')) == 'SPATIAL' || $v['expr_type'] == 'spatial-index') {
                    $type = 'spatial';
                }
                $columns = [];
                foreach ((array) $v['sub_tree'] as $v2) {
                    if ($v2['expr_type'] == 'const') {
                        $name = trim($v2['base_expr'], '"\'`');
                    } elseif ($v2['expr_type'] == 'column-list') {
                        foreach ((array) $v2['sub_tree'] as $v3) {
                            if ($v3['expr_type'] == 'index-column') {
                                $index_col_name = strtolower($v3['no_quotes']['parts'][0]);
                                $columns[$index_col_name] = $index_col_name;
                            }
                        }
                    } elseif ($v2['expr_type'] == 'colref') {
                        $index_col_name = strtolower($v2['no_quotes']['parts'][0]);
                        $columns[$index_col_name] = $index_col_name;
                    }
                }
                if ( ! $name) {
                    $name = 'idx_' . (count((array) $struct['indexes']) + 1);
                }
                if ($name != 'PRIMARY') {
                    $name = strtolower($name);
                }
                $struct['indexes'][$name] = [
                    'name' => $name,
                    'type' => strtolower($type),
                    'columns' => $columns,
                ];
                if ($this->RAW_IN_RESULTS) {
                    $struct['indexes'][$name]['raw'] = $v['base_expr'];
                }
            } elseif ($v['expr_type'] == 'foreign-key') {
                $name = null;
                $columns = [];
                $ref_table = null;
                $ref_columns = [];
                $on_update = null;
                $on_delete = null;
                foreach ((array) $v['sub_tree'] as $v2) {
                    if ($v2['expr_type'] == 'constraint') {
                        $name = trim($v2['sub_tree']['base_expr'], '"\'`');
                    } elseif ($v2['expr_type'] == 'column-list') {
                        foreach ((array) $v2['sub_tree'] as $v3) {
                            if ($v3['expr_type'] == 'index-column') {
                                $index_col_name = strtolower($v3['no_quotes']['parts'][0]);
                                $columns[$index_col_name] = $index_col_name;
                            }
                        }
                    } elseif ($v2['expr_type'] == 'foreign-ref') {
                        foreach ((array) $v2['sub_tree'] as $v3) {
                            if ($v3['expr_type'] == 'table') {
                                $ref_table = $v3['no_quotes']['parts'][0];
                            } elseif ($v3['expr_type'] == 'column-list') {
                                foreach ((array) $v3['sub_tree'] as $v4) {
                                    if ($v4['expr_type'] == 'index-column') {
                                        $ref_col_name = strtolower($v4['no_quotes']['parts'][0]);
                                        $ref_columns[$ref_col_name] = $ref_col_name;
                                    }
                                }
                            }
                        }
                        $on_update = $v2['on_update'];
                        $on_delete = $v2['on_delete'];
                    }
                }
                if ( ! $name) {
                    $name = 'fk_' . (count((array) $struct['foreign_keys']) + 1);
                }
                $name = strtolower($name);
                $struct['foreign_keys'][$name] = [
                    'name' => $name,
                    'columns' => $columns,
                    'ref_table' => $ref_table,
                    'ref_columns' => $ref_columns,
                    'on_update' => $on_update ? strtoupper($on_update) : null,
                    'on_delete' => $on_delete ? strtoupper($on_delete) : null,
                ];
                if ($this->RAW_IN_RESULTS) {
                    $struct['foreign_keys'][$name]['raw'] = $v['base_expr'];
                }
            }
        }
        foreach ((array) $tmp_options as $v) {
            $name = [];
            $val = '';
            foreach ((array) $v['sub_tree'] as $v2) {
                if ($v2['expr_type'] == 'reserved') {
                    $name[] = $v2['base_expr'];
                } elseif ($v2['expr_type'] == 'const') {
                    $val = $v2['base_expr'];
                }
            }
            $name = strtolower(implode(' ', $name));
            if (in_array($name, ['default charset', 'default character set', 'charset', 'character set'])) {
                $name = 'charset';
            } elseif (in_array($name, ['engine'])) {
                $name = 'engine';
            }
            $struct['options'][$name] = $val;
        }
        // Final fixes for compatibility with db utils:
        // If some column exists inside unique key def -> set its attribute "unique = true"
        foreach ((array) $struct['indexes'] as $idx) {
            if ($idx['type'] === 'unique') {
                foreach ((array) $idx['columns'] as $fname) {
                    if (isset($struct['fields'][$fname])) {
                        $struct['fields'][$fname]['unique'] = true;
                    }
                }
            }
        }
        // If some column exists inside primary key def -> set its attribute "primary = true"
        if (isset($struct['indexes']['PRIMARY'])) {
            foreach ((array) $struct['indexes']['PRIMARY']['columns'] as $fname) {
                if (isset($struct['fields'][$fname])) {
                    $struct['fields'][$fname]['primary'] = true;
                }
            }
        }
        return $struct;
    }

Methods starting with _ hooks and helpers; not routable as page actions

public function _get_int_def_length($type)
    public function _get_int_def_length($type)
    {
        $a = [
            'tinyint' => 3,
            'smallint' => 5,
            'mediumint' => 8,
            'int' => 11,
            'bigint' => 20,
        ];
        return ($a[$type] ?? null) ?: 11;
    }

_init

public function _init()
    public function _init()
    {
        require_php_lib('php_sql_parser');
        $this->parser = new \PHPSQLParser\PHPSQLParser();
    }