<commit_before><?php

namespace siestaphp\migrator;

use siestaphp\datamodel\DataModelContainer;
use siestaphp\datamodel\entity\EntityGeneratorSource;
use siestaphp\datamodel\entity\EntitySource;
use siestaphp\driver\ColumnMigrator;
use siestaphp\driver\Connection;
use siestaphp\driver\CreateStatementFactory;

/**
 * Class DatabaseMigrator allows to retrieve alter statement by comparing model/schema with current database setup
 * @package siestaphp\generator
 */
class DatabaseMigrator
{

    /**
     * @var DataModelContainer
     */
    protected $dataModelContainer;

    /**
     * @var EntitySource[]
     */
    protected $databaseModel;

    /**
     * list of table names that are also defined in the current datamodel
     * @var string[]
     */
    protected $neededTableList;

    /**
     * @var Connection
     */
    protected $connection;

    /**
     * @var ColumnMigrator
     */
    protected $columnMigrator;

    /**
     * @var String[]
     */
    protected $statementList;

    /**
     * @var String[]
     */
    protected $alterStatementList;

    /**
     * @param DataModelContainer $dataModelContainer
     * @param Connection $connection
     */
    public function __construct(DataModelContainer $dataModelContainer, Connection $connection)
    {
        $this->dataModelContainer = $dataModelContainer;
        $this->connection = $connection;
        $this->columnMigrator = $connection->getColumnMigrator();
        $this->databaseModel = [];
        $this->neededTableList = [CreateStatementFactory::SEQUENCER_TABLE_NAME];
        $this->alterStatementList = [];
        $this->statementList = [];
    }

    /**
     * @param string $targetNamespace
     * @param string $targetPath
     * @param bool $dropUnusedTables
     *
     * @return String[]
     */
    public function createAlterStatementList($targetNamespace, $targetPath, $dropUnusedTables)
    {
        $factory = $this->connection->getCreateStatementFactory();
        $this->addStatementList($factory->buildSequencer());

        // retrieve entity list as currently setup in the database
        $this->databaseModel = $this->connection->getEntitySourceList($targetNamespace, $targetPath);

        // iterate database entities and migrate entity
        foreach ($this->dataModelContainer->getEntityList() as $entity) {
            $this->migrateEntity($entity);
        }

        // check if unused tables shall be dropped
        if ($dropUnusedTables) {
            $this->dropUnusedTables();
        }

        // done, return list of needed statements
        return $this->statementList;

    }

    /**
     * @param EntityGeneratorSource $modelSource
     *
     * @return void
     */
    private function migrateEntity(EntityGeneratorSource $modelSource)
    {
        $databaseEntity = $this->getDatabaseEntityByModelSource($modelSource);

        // if database entity does not exist, create it
        if ($databaseEntity === null) {

            $this->setupEntityInDatabase($modelSource);
            return;
        }

        // compare database with current model/schema
        $entityMigrator = new EntityMigrator($this->columnMigrator, $databaseEntity, $modelSource);
        $this->alterStatementList = $entityMigrator->createAlterStatementList();

        $this->addStatementList($this->alterStatementList);

        // create stored procedures
        $factory = $this->connection->getCreateStatementFactory();
        $this->addStatementList($factory->buildStoredProceduresStatements($modelSource));

    }

    /**
     * @param EntityGeneratorSource $source
     *
     * @return void
     */
    private function setupEntityInDatabase(EntityGeneratorSource $source)
    {
        $factory = $this->connection->getCreateStatementFactory();

        $this->addStatementList($factory->buildCreateTable($source));

        $this->addStatementList($factory->buildStoredProceduresStatements($source));

        if ($source->isDelimit()) {
            $this->addStatementList($factory->buildCreateDelimitTable($source));
        }
    }

    /**
     * @param EntityGeneratorSource $entityGeneratorSource
     *
     * @return EntitySource|null
     */
    private function getDatabaseEntityByModelSource(EntityGeneratorSource $entityGeneratorSource)
    {
        $tableName = $entityGeneratorSource->getTable();
        foreach ($this->databaseModel as $entity) {
            if ($entity->getTable() === $tableName) {
                // store that this table is actually in use
                $this->neededTableList[] = $tableName;

                if ($entity->isDelimit()) {
                    $this->neededTableList[] = $entityGeneratorSource->getDelimitTable();
                }

                // handle replication

                return $entity;
            }
        }
        return null;
    }

    /**
     * drops unused database tables
     * @return void
     */
    private function dropUnusedTables()
    {
        foreach ($this->databaseModel as $databaseModel) {
            if (in_array($databaseModel->getTable(), $this->neededTableList)) {
                continue;
            }
            $statement = $this->columnMigrator->getDropTableStatement($databaseModel);
            $this->statementList[] = str_replace(ColumnMigrator::TABLE_PLACE_HOLDER, $databaseModel->getTable(), $statement);
        }
    }

    /**
     * @param array $alterStatementList
     *
     * @return void
     */
    private function addStatementList(array $alterStatementList)
    {<commit_msg>migration of delimit tables<commit_after><?php

namespace siestaphp\migrator;

use siestaphp\datamodel\DataModelContainer;
use siestaphp\datamodel\entity\EntityGeneratorSource;
use siestaphp\datamodel\entity\EntitySource;
use siestaphp\driver\ColumnMigrator;
use siestaphp\driver\Connection;
use siestaphp\driver\CreateStatementFactory;

/**
 * Class DatabaseMigrator allows to retrieve alter statement by comparing model/schema with current database setup
 * @package siestaphp\generator
 */
class DatabaseMigrator
{

    /**
     * @var DataModelContainer
     */
    protected $dataModelContainer;

    /**
     * @var EntitySource[]
     */
    protected $databaseModel;

    /**
     * list of table names that are also defined in the current datamodel
     * @var string[]
     */
    protected $neededTableList;

    /**
     * @var Connection
     */
    protected $connection;

    /**
     * @var ColumnMigrator
     */
    protected $columnMigrator;

    /**
     * @var String[]
     */
    protected $statementList;

    /**
     * @var String[]
     */
    protected $alterStatementList;

    /**
     * @param DataModelContainer $dataModelContainer
     * @param Connection $connection
     */
    public function __construct(DataModelContainer $dataModelContainer, Connection $connection)
    {
        $this->dataModelContainer = $dataModelContainer;
        $this->connection = $connection;
        $this->columnMigrator = $connection->getColumnMigrator();
        $this->databaseModel = [];
        $this->neededTableList = [CreateStatementFactory::SEQUENCER_TABLE_NAME];
        $this->alterStatementList = [];
        $this->statementList = [];
    }

    /**
     * @param string $targetNamespace
     * @param string $targetPath
     * @param bool $dropUnusedTables
     *
     * @return String[]
     */
    public function createAlterStatementList($targetNamespace, $targetPath, $dropUnusedTables)
    {
        $factory = $this->connection->getCreateStatementFactory();
        $this->addStatementList($factory->buildSequencer());

        // retrieve entity list as currently setup in the database
        $this->databaseModel = $this->connection->getEntitySourceList($targetNamespace, $targetPath);

        // iterate database entities and migrate entity
        foreach ($this->dataModelContainer->getEntityList() as $entity) {
            $this->migrateEntity($entity);
        }

        // check if unused tables shall be dropped
        if ($dropUnusedTables) {
            $this->dropUnusedTables();
        }

        // done, return list of needed statements
        return $this->statementList;

    }

    /**
     * @param EntityGeneratorSource $modelSource
     *
     * @return void
     */
    private function migrateEntity(EntityGeneratorSource $modelSource)
    {
        $databaseEntity = $this->getDatabaseEntityByModelSource($modelSource);

        // if database entity does not exist, create it
        if ($databaseEntity === null) {

            $this->setupEntityInDatabase($modelSource);
            return;
        }

        // compare database with current model/schema
        $entityMigrator = new EntityMigrator($this->columnMigrator, $databaseEntity, $modelSource);
        $this->alterStatementList = $entityMigrator->createAlterStatementList();

        $this->addStatementList($this->alterStatementList);

        // create stored procedures
        $factory = $this->connection->getCreateStatementFactory();
        $this->addStatementList($factory->buildStoredProceduresStatements($modelSource));

    }

    /**
     * @param EntityGeneratorSource $source
     *
     * @return void
     */
    private function setupEntityInDatabase(EntityGeneratorSource $source)
    {
        $factory = $this->connection->getCreateStatementFactory();

        $this->addStatementList($factory->buildCreateTable($source));

        $this->addStatementList($factory->buildStoredProceduresStatements($source));

        if ($source->isDelimit()) {
            $this->addStatementList($factory->buildCreateDelimitTable($source));
        }
    }

    /**
     * @param EntityGeneratorSource $entityGeneratorSource
     *
     * @return EntitySource|null
     */
    private function getDatabaseEntityByModelSource(EntityGeneratorSource $entityGeneratorSource)
    {
        $tableName = $entityGeneratorSource->getTable();
        foreach ($this->databaseModel as $entity) {
            if ($entity->getTable() === $tableName) {
                // store that this table is actually in use
                $this->neededTableList[] = $tableName;

                if ($entityGeneratorSource->isDelimit()) {
                    $this->neededTableList[] = $entityGeneratorSource->getDelimitTable();
                }

                // TODO: handle replication

                return $entity;
            }
        }
        return null;
    }

    /**
     * drops unused database tables
     * @return void
     */
    private function dropUnusedTables()
    {
        foreach ($this->databaseModel as $databaseModel) {
            if (in_array($databaseModel->getTable(), $this->neededTableList)) {
                continue;
            }
            $statement = $this->columnMigrator->getDropTableStatement($databaseModel);
            $this->statementList[] = str_replace(ColumnMigrator::TABLE_PLACE_HOLDER, $databaseModel->getTable(), $statement);
        }
    }

    /**
     * @param array $alterStatementList
     *
     * @return void
     */
    private function addStatementList(array $alterStatementList)
    {
{'fraction_non_alphanumeric': 0.1122311223112231, 'fraction_numerical': 0.0, 'mean_word_length': 2.6066212268743914, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 111, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7048787', 'n_tokens_mistral': 2936, 'n_tokens_neox': 2837, 'n_words': 574}