MySQL или Firebird: Онлайн конвертер mysql2firebird

В долгоиграющем проекте phpRecaller появилась очередная увесистая задача: перевести проект с базы данных mysql 5 (myisam), на firebird 2.5.

Драйвер firebird для codeigniter я нашел без проблем на просторах интерета, правда некие допилы совершить пришлось, например чтобы добавить имитацию last_insert_id и num_rows для firebird, кстати я об этом как-нибудь расскажу…

Но помимо драйвера, пришлось еще и конвертировать базу. Таблиц всего лишь 29, вес тоже небольшой, не превышал ~70mb. Но продолбался я изрядно, и никакие режимы совместимости из опробованных мной в скромных настройках phpMyAdmin мне не помогли… точнее почти не помогли.

Как бы там ни было, конвертация из sql в sql была мучительной и не могла решится постоянной заменой строк в тексте через встроеные средства моего Komodo Edit, даже с использованием макросов.

Решение я нашел на github, в проекте mysql2firebird некого Popa Marius Adrian из Румынии. Я даже форкнул его проект, добавив свои пять копеек добавление дефолтных значений в firebird.

Его проект кстати работает из командной строки, это и не удивительно, ведь оперировать даже 10 мегабайтовым дампом в текстовом поле ввода это довольно мучительное дело. Тем не менее мне для конвертации структуры очень помогла нижеприведенная вариация конвертера. Вы можете использовать ее, только помните, что длина вводимых данных ограничена 4096 символами юникода.

Если кому-то будет интересно, то вот исходный код адаптированной под веб версии:

<?php
// Copyright (c) 2011 Milan Babuskov (mbabuskov@yahoo.com)
// This software is released under GNU GPL licence version 2, please see the
// contributor Anton_Gorodezkiy (antongorodezkiy@gmail.com)

ini_set('magic_quotes_gpc',0);

if ( isset($_POST['sql']) && $_POST['sql'])
{
    $sql = substr($_POST['sql'],0,4096);
    $sql = strip_tags($sql);
    $sql = stripcslashes($_POST['sql']);
    // split the script into statements and process each one
    $statements = explode(';', $sql);
    foreach ($statements as $s)
    {
        $s = str_replace('`', '"', $s);
        $s = str_replace('\'', '"', $s);
        $s = str_replace('ENGINE=MyISAM', '', $s);
        $s = str_replace('SET SQL_MODE', '-- SET SQL_MODE', $s);

        // could be done better
        $s = str_replace('DEFAULT CHARSET=utf8', '', $s);
        $s = str_replace('COLLATE=utf8_unicode_ci', '', $s);
        $s = str_replace('collate utf8_unicode_ci', '', $s);
        $s = str_replace('CREATE TABLE IF NOT EXISTS', 'CREATE TABLE', $s);
        $s = str_replace(' unsigned', ' /* WARNING: unsigned */', $s);

        // convert datatypes
        $s = str_replace(' int(2)', ' integer', $s);
        $s = str_replace(' int(11)', ' integer', $s);
        $s = str_replace(' int(10)', ' integer', $s);
        $s = str_replace(' tinyint(1)', ' smallint /* WARNING: tinyint */', $s);
        $s = str_replace(' tinyint(2)', ' smallint /* WARNING: tinyint */', $s);
        $s = str_replace(' tinyint(4)', ' smallint /* WARNING: tinyint */', $s);
        $s = str_replace(' mediumint(4)', ' integer /* WARNING: mediumint */', $s);
        $s = str_replace(' mediumint(8)', ' integer /* WARNING: mediumint */', $s);
        $s = str_replace(' smallint(6)', ' smallint', $s);
        $s = str_replace(' bigint(20)', ' bigint', $s);
        //$s = str_replace(' bigint', ' int64', $s);
        $s = str_replace(' datetime', ' timestamp', $s);
        $s = str_replace(' longtext', ' varchar(8000)', $s);
        $s = str_replace(' text', ' varchar(8000)', $s);
        $s = str_replace(' mediumtext', ' varchar(8000)', $s);

        // break statements into lines
        $sp = explode("\n", $s);
        $ac_field = false;
        $ac_value = 0;
        $keys = array();
        $lines = array();
        foreach ($sp as $line)
        {
            if (strpos($line, 'NOT NULL') !== false)
            {
                $line = trim(str_replace('NOT NULL', '', $line));
                $tline = trim($line, ',');

                if (!$_POST['delete_no_null'])
                {
                    if ($tline == $line)
                        $line .= 'NOT NULL';
                    else
                        $line = $tline.' NOT NULL,';
                }
            }

            if ($_POST['delete_null'])
            {
                $line = trim(str_replace('NULL', '', $line));
            }

            // break line into fields
            $fields = explode(' ', trim($line));
            if (strpos($line, 'CREATE TABLE') !== false)
            {
                $tablename = trim($fields[2]);
                $table = str_replace('"', '', $tablename);
            }

            if (strpos($line, 'auto_increment') !== false) // extract field name
                $ac_field = trim($fields[0]);
            $line = str_replace('auto_increment', '', $line);

            $fields = explode(' ', trim($line));
            if ($fields[0] == 'KEY')
            {
                $k = " create index idx_".$table.'_'.count($keys)." on $tablename ";
                unset($fields[1]);
                unset($fields[0]);
                $keys[] = trim($k.join(' ', $fields), ',');
                continue;
            }

            foreach ($fields as $ix => $f)
            {

                if (strpos($f, 'AUTO_INCREMENT=') !== false)
                {
                    $p = explode('=', trim($f));
                    $ac_value = $p[1];
                    $fields[$ix] = '';
                }

                if (trim($f) == 'DEFAULT' && strpos($fields[$ix+1], 'CHARSET=') !== false)
                {
                    $fields[$ix] = '';
                    $fields[$ix+1] = '';
                }

                if (trim($f) == 'UNIQUE' && trim($fields[$ix+1]) == 'KEY')
                {
                    $fields[$ix+1] = '';
                    $fields[$ix+2] = '';
                }

                // default values
                if (strtoupper(trim($f)) == 'DEFAULT')
                {

                    // set default values for firebird
                    if ($_POST['defaults_on'])
                    {
                        $temp_val = str_replace(array(',','/'),'',$fields[$ix+1]);

                        if ( trim($temp_val) )
                            $set_default_queries[] = "alter table $tablename alter ".trim($fields[0])." set default '".$temp_val."';";
                        else
                            $fields[$ix+1] = ',';

                        $fields[$ix] = '';

                    }
                    else
                    {
                        $fields[$ix] = '';
                        $fields[$ix+1] = '';
                    }
                }

                // default values
                /*if (strtoupper(trim($f)) == 'COMMENT')
                {

                    // replace comments
                    if ($_POST['delete_comment'])
                    {
                        $fields[$ix] = '';
                        $fields[$ix+1] = '';
                    }
                }*/

            }

            $lines[] = trim(join(' ', $fields));
        }

        // remove trailing comma at the end of create table
        foreach ($lines as $ix => $l)
            if ($ix > 0 && substr($l, 0, 1) == ')') // end of CREATE TABLE
                $lines[$ix-1] = trim($lines[$ix-1], ',');

        $s = join("\n", $lines);
        if (trim($s) != '')
            $output .= $s.';';

        foreach ($keys as $k)
            $output .= "\n$k;";

        if ($ac_field !== false)
        {
            if ($_POST['create_generator'])
            {
                /*$output .= "
                    create generator gen_$table;
                    set generator gen_$table to $ac_value;
                    set term !! ;
                    create trigger t_$table for $tablename before insert position 0
                    as begin
                    if (new.$ac_field is null) then
                    new.$ac_field = gen_id(gen_$table, 1);
                    RDB\$SET_CONTEXT('USER_SESSION', 'LAST_INSERT_ID', new.$ac_field);
                    end!!
                    set term ; !!
                ";*/

                $output .= "
                    CREATE GENERATOR GEN_$table;

                    SET TERM !! ;
                    CREATE TRIGGER t_$table FOR $tablename
                    ACTIVE BEFORE INSERT POSITION 0
                    AS
                    DECLARE VARIABLE tmp DECIMAL(18,0);
                    BEGIN
                      IF (NEW.$ac_field IS NULL) THEN
                        NEW.$ac_field = GEN_ID(GEN_$table, 1);
                      ELSE
                      BEGIN
                        tmp = GEN_ID(GEN_$table, 0);
                        if (tmp < new.$ac_field) then
                          tmp = GEN_ID(GEN_$table, new.$ac_field-tmp);
                      END
                    END!!
                    SET TERM ; !!
                ";
            }
        }

    }

    $output = str_replace('""','NULL',$output);

    if ($_POST['defaults_on'] && isset($set_default_queries))
            $output .= implode("\n",$set_default_queries);

    if ($_POST['last_insert_id'])
    {
        $output .= "
        set term !! ;
        CREATE PROCEDURE LAST_INSERT_ID RETURNING (ID BIGINT) AS
        BEGIN
        id = RDB\$GET_CONTEXT('USER_SESSION', 'LAST_INSERT_ID');
        suspend;
        END
        set term ; !!
        ";
    }
}
?>
<style>
    .sql_container
    {
        margin:0 auto;
        width:1000px;
    }

    .sql, .output
    {
        width:48%;
        height:300px;
        font-size:10pt;
        overflow:scroll;
        white-space:nowrap;
    }

    .output
    {
        margin-left:3%;
    }

    .convert_button
    {
        text-align:center;
    }

    label
    {
        display:block;
    }

    .center
    {
        text-align:center;
    }
</style>
<form method="post">
    <div class="sql_container">
        <textarea class="sql" name="sql" placeholder="mysql sql source..."><?=( isset($sql) ? $sql : '' )?></textarea>
        <textarea class="output" placeholder="firebird sql output..."><?=( isset($output) ? $output : '' )?></textarea>
    </div>
    <div class="center">
        <label>
            Создать PROCEDURE LAST_INSERT_ID
            <input type="hidden" name="last_insert_id" value="0"/>
            <input type="checkbox" name="last_insert_id" value="1"/>
        </label>

        <label>
            Создать GENERATOR
            <input type="hidden" name="create_generator" value="0"/>
            <input type="checkbox" checked="checked" name="create_generator" value="1"/>
        </label>

        <label>
            Удалять NOT NULL
            <input type="hidden" name="delete_no_null" value="0"/>
            <input type="checkbox" checked="checked" name="delete_no_null" value="1"/>
        </label>

        <label>
            Удалять NULL
            <input type="hidden" name="delete_null" value="0"/>
            <input type="checkbox" checked="checked" name="delete_null" value="1"/>
        </label>

        <!--<label>
            Удалять COMMENT
            <input type="hidden" name="delete_comment" value="0"/>
            <input type="checkbox" checked="checked" name="delete_comment" value="1"/>
        </label>-->

        <label>
            Включить DEFAULTS
            <input type="hidden" name="defaults_on" value="0"/>
            <input type="checkbox" checked="checked" name="defaults_on" value="1"/>
        </label>

    </div>
    <div class="convert_button"><button type="submit">Convert</button></div>
</form>
<div class="center">
    <a href="https://github.com/mariuz/mysql2firebird">
        original (c) Popa Marius Adrian <img src="/img/github.png" alt="github"/>
    </a>
    Адаптировано для веб Anton_Gorodezkiy
</div>
Полезно(0)Бесполезно(0)

Добавить комментарий