<commit_before>/*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2017 <NAME> (<EMAIL>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jkiss.dbeaver.model.impl.edit;

import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.edit.*;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.exec.DBCExecutionPurpose;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.exec.DBCTransactionManager;
import org.jkiss.dbeaver.model.messages.ModelMessages;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;

import java.util.*;

/**
 * DBECommandContextImpl
 */
public abstract class AbstractCommandContext implements DBECommandContext {

    private static final Log log = Log.getLog(AbstractCommandContext.class);

    private final DBCExecutionContext executionContext;
    private final List<CommandInfo> commands = new ArrayList<>();
    private final List<CommandInfo> undidCommands = new ArrayList<>();
    private List<CommandQueue> commandQueues;

    private final Map<Object, Object> userParams = new HashMap<>();
    private final List<DBECommandListener> listeners = new ArrayList<>();

    private final boolean atomic;

    /**
     * Creates new context
     * @param executionContext Execution context
     * @param atomic atomic context reflect commands in UI only after all commands were executed. Non-atomic
     *               reflects each command at the moment it executed
     */
    public AbstractCommandContext(DBCExecutionContext executionContext, boolean atomic)
    {
        this.executionContext = executionContext;
        this.atomic = atomic;
    }

    @Override
    public DBCExecutionContext getExecutionContext()
    {
        return executionContext;
    }

    @Override
    public boolean isDirty()
    {
        synchronized (commands) {
            return !getCommandQueues().isEmpty();
        }
    }

    @Override
    public void saveChanges(DBRProgressMonitor monitor) throws DBException {
        if (!executionContext.isConnected()) {
            throw new DBException("Context [" + executionContext.getContextName() + "] isn't connected to the database");
        }
        List<CommandQueue> commandQueues = getCommandQueues();

        // Validate commands
        for (CommandQueue queue : commandQueues) {
            for (CommandInfo cmd : queue.commands) {
                cmd.command.validateCommand();
            }
        }

        // Execute commands
        List<CommandInfo> executedCommands = new ArrayList<>();
        try {
            for (CommandQueue queue : commandQueues) {
                // Make list of not-executed commands
                for (int i = 0; i < queue.commands.size(); i++) {
                    if (monitor.isCanceled()) {
                        break;
                    }

                    CommandInfo cmd = queue.commands.get(i);
                    while (cmd.mergedBy != null) {
                        cmd = cmd.mergedBy;
                    }
                    if (!cmd.executed) {
                        // Persist changes
                        //if (CommonUtils.isEmpty(cmd.persistActions)) {
                            DBEPersistAction[] persistActions = cmd.command.getPersistActions();
                            if (!ArrayUtils.isEmpty(persistActions)) {
                                cmd.persistActions = new ArrayList<>(persistActions.length);
                                for (DBEPersistAction action : persistActions) {
                                    cmd.persistActions.add(new PersistInfo(action));
                                }
                            }
                        //}
                        if (!CommonUtils.isEmpty(cmd.persistActions)) {
                            try (DBCSession session = openCommandPersistContext(monitor, cmd.command)) {
                                DBException error = null;
                                for (PersistInfo persistInfo : cmd.persistActions) {
                                    DBEPersistAction.ActionType actionType = persistInfo.action.getType();
                                    if (persistInfo.executed && actionType == DBEPersistAction.ActionType.NORMAL) {
                                        continue;
                                    }
                                    if (monitor.isCanceled()) {
                                        break;
                                    }
                                    try {
                                        if (error == null || actionType == DBEPersistAction.ActionType.FINALIZER) {
                                            queue.objectManager.executePersistAction(session, cmd.command, persistInfo.action);
                                        }
                                        persistInfo.executed = true;
                                    } catch (DBException e) {
                                        persistInfo.error = e;
                                        persistInfo.executed = false;
                                        if (actionType != DBEPersistAction.ActionType.OPTIONAL) {
                                            error = e;
                                        }
                                    }
                                }
                                if (error != null) {
                                    throw error;
                                }

                                // Commit metadata changes
                                DBCTransactionManager txnManager = DBUtils.getTransactionManager(session.getExecutionContext());
                                if (txnManager != null && !txnManager.isAutoCommit()) {
                                    txnManager.commit(session);
                                }
                            }
                            cmd.executed = true;
                        }
                    }
                    if (cmd.executed) {
                        // Remove only executed commands
                        // Commands which do not perform any persist actions<commit_msg>#1706 Command context save in transactional mode<commit_after>/*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2017 <NAME> (<EMAIL>)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jkiss.dbeaver.model.impl.edit;

import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPObject;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.edit.*;
import org.jkiss.dbeaver.model.exec.*;
import org.jkiss.dbeaver.model.messages.ModelMessages;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;

import java.util.*;

/**
 * DBECommandContextImpl
 */
public abstract class AbstractCommandContext implements DBECommandContext {

    private static final Log log = Log.getLog(AbstractCommandContext.class);

    private final DBCExecutionContext executionContext;
    private final List<CommandInfo> commands = new ArrayList<>();
    private final List<CommandInfo> undidCommands = new ArrayList<>();
    private List<CommandQueue> commandQueues;

    private final Map<Object, Object> userParams = new HashMap<>();
    private final List<DBECommandListener> listeners = new ArrayList<>();

    private final boolean atomic;

    /**
     * Creates new context
     * @param executionContext Execution context
     * @param atomic atomic context reflect commands in UI only after all commands were executed. Non-atomic
     *               reflects each command at the moment it executed
     */
    public AbstractCommandContext(DBCExecutionContext executionContext, boolean atomic)
    {
        this.executionContext = executionContext;
        this.atomic = atomic;
    }

    @Override
    public DBCExecutionContext getExecutionContext()
    {
        return executionContext;
    }

    @Override
    public boolean isDirty()
    {
        synchronized (commands) {
            return !getCommandQueues().isEmpty();
        }
    }

    @Override
    public void saveChanges(DBRProgressMonitor monitor) throws DBException {
        if (!executionContext.isConnected()) {
            throw new DBException("Context [" + executionContext.getContextName() + "] isn't connected to the database");
        }

        // Execute commands in transaction
        DBCTransactionManager txnManager = DBUtils.getTransactionManager(executionContext);
        boolean oldAutoCommit = false;
        if (txnManager != null) {
            oldAutoCommit = txnManager.isAutoCommit();
            if (oldAutoCommit) {
                try {
                    txnManager.setAutoCommit(monitor, false);
                } catch (DBCException e) {
                    log.warn("Can't switch to transaction mode", e);
                }
            }
        }
        try {
            executeCommands(monitor);

            // Commit changes
            if (txnManager != null) {
                try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.UTIL, "Commit script transaction")) {
                    txnManager.commit(session);
                } catch (DBCException e1) {
                    log.warn("Can't commit script transaction", e1);
                }
            }
        } catch (Throwable e) {
            // Rollback changes
            if (txnManager != null) {
                try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.UTIL, "Rollback script transaction")) {
                    txnManager.rollback(session, null);
                } catch (DBCException e1) {
                    log.warn("Can't rollback transaction after error", e);
                }
            }
            throw e;
        } finally {
            if (txnManager != null && oldAutoCommit) {
                try {
                    txnManager.setAutoCommit(monitor, true);
                } catch (DBCException e) {
                    log.warn("Can't switch back to auto-commit mode", e);
                }
            }
        }
    }

    private void executeCommands(DBRProgressMonitor monitor) throws DBException {
        List<CommandQueue> commandQueues = getCommandQueues();

        // Validate commands
        for (CommandQueue queue : commandQueues) {
            for (CommandInfo cmd : queue.commands) {
                cmd.command.validateCommand();
            }
        }

        // Execute commands
        List<CommandInfo> executedCommands = new ArrayList<>();
        try {
            for (CommandQueue queue : commandQueues) {
                // Make list of not-executed commands
                for (int i = 0; i < queue.commands.size(); i++) {
                    if (monitor.isCanceled()) {
                        break;
                    }

                    CommandInfo cmd = queue.commands.get(i);
                    while (cmd.mergedBy != null) {
                        cmd = cmd.mergedBy;
                    }
                    if (!cmd.executed) {
                        // Persist changes
                        //if (CommonUtils.isEmpty(cmd.persistActions)) {
                            DBEPersistAction[] persistActions = cmd.command.getPersistActions();
                            if (!ArrayUtils.isEmpty(persistActions)) {
                                cmd.persistActions = new ArrayList<>(persistActions.length);
                                for (DBEPersistAction action : persistActions) {
                                    cmd.persistActions.add(new PersistInfo(action));
                                }
                            }
                        //}
                        if (!CommonUtils.isEmpty(cmd.persistActions)) {
                            try (DBCSession session = openCommandPersistContext(monitor, cmd.command)) {
                                DBException error = null;
                                for (PersistInfo persistInfo : cmd.persistActions) {
                                    DBEPersistAction.ActionType actionType = persistInfo.action.getType();
                                    if (persistInfo.executed && actionType == DBEPersistAction.ActionType.NORMAL) {
                                        continue;
                                    }
                                    if (monitor.isCanceled()) {
                                        break;
                                    }
                                    try {
                                        if (error == null || actionType == DBEPersistAction.ActionType.FINALIZER) {
                                            queue.objectManager.executePersistAction(session, cmd.command, persistInfo.action);
                                        }
                                        persistInfo.executed = true;
                                    } catch (DBException e) {
                                        persistInfo.error = e;
                                        persistInfo.executed = false;
                                        if (actionType != DBEPersistAction.ActionType.OPTIONAL) {
                                            error = e;
                                        }
                                    }
                                }
                                if (error != null) {
                                    throw error;
                                }
                            }
                            cmd.executed = true;
                        }
                    }
                    if (cmd.executed) {
                        // Remove only executed commands
                        // Commands which do not perform any persist actions
{'fraction_non_alphanumeric': 0.07890381942146026, 'fraction_numerical': 0.0021844178195538494, 'mean_word_length': 1.4301109860061123, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 59, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1121726', 'n_tokens_mistral': 3479, 'n_tokens_neox': 3337, 'n_words': 866}