# Buggy code
def execute(
    self,
    tool,
    trans,
    incoming=None,
    return_job=False,
    set_output_hid=True,
    history=None,
    job_params=None,
    rerun_remap_job_id=None,
    execution_cache=None,
    dataset_collection_elements=None,
    completed_job=None,
    collection_info=None,
):
    """
    Executes a tool, creating job and tool outputs, associating them, and
    submitting the job to the job queue. If history is not specified, use
    trans.history as destination for tool's output datasets.
    """
    trans.check_user_activation()
    incoming = incoming or {}
    self._check_access(tool, trans)
    app = trans.app
    if execution_cache is None:
        execution_cache = ToolExecutionCache(trans)
    current_user_roles = execution_cache.current_user_roles
    history, inp_data, inp_dataset_collections, preserved_tags, all_permissions = (
        self._collect_inputs(
            tool, trans, incoming, history, current_user_roles, collection_info
        )
    )
    # Build name for output datasets based on tool name and input names
    on_text = self._get_on_text(inp_data)

    # format='input" previously would give you a random extension from
    # the input extensions, now it should just give "input" as the output
    # format.
    input_ext = "data" if tool.profile < 16.04 else "input"
    input_dbkey = incoming.get("dbkey", "?")
    for name, data in reversed(list(inp_data.items())):
        if not data:
            data = NoneDataset(datatypes_registry=app.datatypes_registry)
            continue

        # Convert LDDA to an HDA.
        if isinstance(data, LibraryDatasetDatasetAssociation) and not completed_job:
            data = data.to_history_dataset_association(None)
            inp_data[name] = data

        if tool.profile < 16.04:
            input_ext = data.ext

        if data.dbkey not in [None, "?"]:
            input_dbkey = data.dbkey

        identifier = getattr(data, "element_identifier", None)
        if identifier is not None:
            incoming["%s|__identifier__" % name] = identifier

    # Collect chromInfo dataset and add as parameters to incoming
    (chrom_info, db_dataset) = execution_cache.get_chrom_info(tool.id, input_dbkey)

    if db_dataset:
        inp_data.update({"chromInfo": db_dataset})
    incoming["chromInfo"] = chrom_info

    if not completed_job:
        # Determine output dataset permission/roles list
        existing_datasets = [inp for inp in inp_data.values() if inp]
        if existing_datasets:
            output_permissions = app.security_agent.guess_derived_permissions(
                all_permissions
            )
        else:
            # No valid inputs, we will use history defaults
            output_permissions = app.security_agent.history_get_default_permissions(
                history
            )

    # Add the dbkey to the incoming parameters
    incoming["dbkey"] = input_dbkey
    # wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed
    wrapped_params = self._wrapped_params(trans, tool, incoming, inp_data)

    out_data = OrderedDict()
    input_collections = dict((k, v[0][0]) for k, v in inp_dataset_collections.items())
    output_collections = OutputCollections(
        trans,
        history,
        tool=tool,
        tool_action=self,
        input_collections=input_collections,
        dataset_collection_elements=dataset_collection_elements,
        on_text=on_text,
        incoming=incoming,
        params=wrapped_params.params,
        job_params=job_params,
        tags=preserved_tags,
    )

    # Keep track of parent / child relationships, we'll create all the
    # datasets first, then create the associations
    parent_to_child_pairs = []
    child_dataset_names = set()
    object_store_populator = ObjectStorePopulator(app)

    def handle_output(name, output, hidden=None):
        if output.parent:
            parent_to_child_pairs.append((output.parent, name))
            child_dataset_names.add(name)
        # What is the following hack for? Need to document under what
        # conditions can the following occur? (james@bx.psu.edu)
        # HACK: the output data has already been created
        #      this happens i.e. as a result of the async controller
        if name in incoming:
            dataid = incoming[name]
            data = trans.sa_session.query(app.model.HistoryDatasetAssociation).get(
                dataid
            )
            assert data is not None
            out_data[name] = data
        else:
            ext = determine_output_format(
                output,
                wrapped_params.params,
                inp_data,
                inp_dataset_collections,
                input_ext,
                python_template_version=tool.python_template_version,
            )
            create_datasets = True
            dataset = None

            if completed_job:
                for output_dataset in completed_job.output_datasets:
                    if output_dataset.name == name:
                        create_datasets = False
                        completed_data = output_dataset.dataset
                        dataset = output_dataset.dataset.dataset
                        break

            data = app.model.HistoryDatasetAssociation(
                extension=ext,
                dataset=dataset,
                create_dataset=create_datasets,
                flush=False,
            )
            if create_datasets:
                from_work_dir = output.from_work_dir
                if from_work_dir is not None:
                    data.dataset.created_from_basename = os.path.basename(from_work_dir)
            if hidden is None:
                hidden = output.hidden
            if (
                not hidden and dataset_collection_elements is not None
            ):  # Mapping over a collection - hide datasets
                hidden = True
            if hidden:
                data.visible = False
            if (
                dataset_collection_elements is not None
                and name in dataset_collection_elements
            ):
                dataset_collection_elements[name].hda = data
            trans.sa_session.add(data)
            if not completed_job:
                trans.app.security_agent.set_all_dataset_permissions(
                    data.dataset, output_permissions, new=True
                )
        data.copy_tags_to(preserved_tags)

        if (
            not completed_job
            and trans.app.config.legacy_eager_objectstore_initialization
        ):
            # Must flush before setting object store id currently.
            trans.sa_session.flush()
            object_store_populator.set_object_store_id(data)

        # This may not be neccesary with the new parent/child associations
        data.designation = name
        # Copy metadata from one of the inputs if requested.

        # metadata source can be either a string referencing an input
        # or an actual object to copy.
        metadata_source = output.metadata_source
        if metadata_source:
            if isinstance(metadata_source, string_types):
                metadata_source = inp_data.get(metadata_source)

        if metadata_source is not None:
            data.init_meta(copy_from=metadata_source)
        else:
            data.init_meta()
        # Take dbkey from LAST input
        data.dbkey = str(input_dbkey)
        # Set state
        if completed_job:
            data.blurb = completed_data.blurb
            data.peek = completed_data.peek
            data._metadata = completed_data._metadata
        else:
            data.blurb = "queued"
        # Set output label
        data.name = self.get_output_name(
            output,
            data,
            tool,
            on_text,
            trans,
            incoming,
            history,
            wrapped_params.params,
            job_params,
        )
        # Store output
        out_data[name] = data
        if output.actions:
            # Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
            output_action_params = dict(out_data)
            output_action_params.update(incoming)
            output.actions.apply_action(data, output_action_params)
        # Also set the default values of actions of type metadata
        self.set_metadata_defaults(
            output,
            data,
            tool,
            on_text,
            trans,
            incoming,
            history,
            wrapped_params.params,
            job_params,
        )
        # Flush all datasets at once.
        return data

    for name, output in tool.outputs.items():
        if not filter_output(output, incoming):
            handle_output_timer = ExecutionTimer()
            if output.collection:
                collections_manager = app.dataset_collections_service
                element_identifiers = []
                known_outputs = output.known_outputs(
                    input_collections, collections_manager.type_registry
                )
                created_element_datasets = []
                # Just to echo TODO elsewhere - this should be restructured to allow
                # nested collections.
                for output_part_def in known_outputs:
                    # Add elements to top-level collection, unless nested...
                    current_element_identifiers = element_identifiers
                    current_collection_type = output.structure.collection_type

                    for parent_id in output_part_def.parent_ids or []:
                        # TODO: replace following line with formal abstractions for doing this.
                        current_collection_type = ":".join(
                            current_collection_type.split(":")[1:]
                        )
                        name_to_index = dict(
                            (value["name"], index)
                            for (index, value) in enumerate(current_element_identifiers)
                        )
                        if parent_id not in name_to_index:
                            if parent_id not in current_element_identifiers:
                                index = len(current_element_identifiers)
                                current_element_identifiers.append(
                                    dict(
                                        name=parent_id,
                                        collection_type=current_collection_type,
                                        src="new_collection",
                                        element_identifiers=[],
                                    )
                                )
                            else:
                                index = name_to_index[parent_id]
                        current_element_identifiers = current_element_identifiers[
                            index
                        ]["element_identifiers"]

                    effective_output_name = output_part_def.effective_output_name
                    element = handle_output(
                        effective_output_name, output_part_def.output_def, hidden=True
                    )
                    created_element_datasets.append(element)
                    # TODO: this shouldn't exist in the top-level of the history at all
                    # but for now we are still working around that by hiding the contents
                    # there.
                    # Following hack causes dataset to no be added to history...
                    child_dataset_names.add(effective_output_name)
                    trans.sa_session.add(element)
                    current_element_identifiers.append(
                        {
                            "__object__": element,
                            "name": output_part_def.element_identifier,
                        }
                    )

                history.add_datasets(
                    trans.sa_session,
                    created_element_datasets,
                    set_hid=set_output_hid,
                    quota=False,
                    flush=True,
                )
                if output.dynamic_structure:
                    assert not element_identifiers  # known_outputs must have been empty
                    element_kwds = dict(
                        elements=collections_manager.ELEMENTS_UNINITIALIZED
                    )
                else:
                    element_kwds = dict(element_identifiers=element_identifiers)
                output_collections.create_collection(
                    output=output, name=name, **element_kwds
                )
                log.info(
                    "Handled collection output named %s for tool %s %s"
                    % (name, tool.id, handle_output_timer)
                )
            else:
                handle_output(name, output)
                log.info(
                    "Handled output named %s for tool %s %s"
                    % (name, tool.id, handle_output_timer)
                )

    add_datasets_timer = ExecutionTimer()
    # Add all the top-level (non-child) datasets to the history unless otherwise specified
    datasets_to_persist = []
    for name, data in out_data.items():
        if (
            name not in child_dataset_names and name not in incoming
        ):  # don't add children; or already existing datasets, i.e. async created
            datasets_to_persist.append(data)
    # Set HID and add to history.
    # This is brand new and certainly empty so don't worry about quota.
    history.add_datasets(
        trans.sa_session,
        datasets_to_persist,
        set_hid=set_output_hid,
        quota=False,
        flush=False,
    )

    # Add all the children to their parents
    for parent_name, child_name in parent_to_child_pairs:
        parent_dataset = out_data[parent_name]
        child_dataset = out_data[child_name]
        parent_dataset.children.append(child_dataset)

    log.info("Added output datasets to history %s" % add_datasets_timer)
    job_setup_timer = ExecutionTimer()
    # Create the job object
    job, galaxy_session = self._new_job_for_session(trans, tool, history)
    self._record_inputs(trans, tool, job, incoming, inp_data, inp_dataset_collections)
    self._record_outputs(job, out_data, output_collections)
    job.object_store_id = object_store_populator.object_store_id
    if job_params:
        job.params = dumps(job_params)
    if completed_job:
        job.set_copied_from_job_id(completed_job.id)
    trans.sa_session.add(job)
    # Now that we have a job id, we can remap any outputs if this is a rerun and the user chose to continue dependent jobs
    # This functionality requires tracking jobs in the database.
    if app.config.track_jobs_in_database and rerun_remap_job_id is not None:
        self._remap_job_on_rerun(
            trans=trans,
            galaxy_session=galaxy_session,
            rerun_remap_job_id=rerun_remap_job_id,
            current_job=job,
            out_data=out_data,
        )
    log.info(
        "Setup for job %s complete, ready to be enqueued %s"
        % (job.log_str(), job_setup_timer)
    )

    # Some tools are not really executable, but jobs are still created for them ( for record keeping ).
    # Examples include tools that redirect to other applications ( epigraph ).  These special tools must
    # include something that can be retrieved from the params ( e.g., REDIRECT_URL ) to keep the job
    # from being queued.
    if "REDIRECT_URL" in incoming:
        # Get the dataset - there should only be 1
        for name in inp_data.keys():
            dataset = inp_data[name]
        redirect_url = tool.parse_redirect_url(dataset, incoming)
        # GALAXY_URL should be include in the tool params to enable the external application
        # to send back to the current Galaxy instance
        GALAXY_URL = incoming.get("GALAXY_URL", None)
        assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config."
        redirect_url += "&GALAXY_URL=%s" % GALAXY_URL
        # Job should not be queued, so set state to ok
        job.set_state(app.model.Job.states.OK)
        job.info = "Redirected to: %s" % redirect_url
        trans.sa_session.add(job)
        trans.sa_session.flush()
        trans.response.send_redirect(
            url_for(
                controller="tool_runner", action="redirect", redirect_url=redirect_url
            )
        )
    else:
        # Dispatch to a job handler. enqueue() is responsible for flushing the job
        app.job_manager.enqueue(job, tool=tool)
        trans.log_event(
            "Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id
        )
        return job, out_data


# Traceback (sqlalchemy.exc.ProgrammingError)
2020-01-20T10:34:14.9786332Z 2020-01-20 10:34:14,974 ERROR [galaxy.tools] Exception caught while attempting tool execution:
2020-01-20T10:34:14.9787128Z Traceback (most recent call last):
2020-01-20T10:34:14.9789375Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1246, in _execute_context
2020-01-20T10:34:14.9790073Z     cursor, statement, parameters, context
2020-01-20T10:34:14.9790998Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 581, in do_execute
2020-01-20T10:34:14.9791567Z     cursor.execute(statement, parameters)
2020-01-20T10:34:14.9792168Z psycopg2.errors.UndefinedFunction: operator does not exist: integer = boolean
2020-01-20T10:34:14.9792461Z LINE 3: WHERE history_dataset_association.id = true
2020-01-20T10:34:14.9792893Z                                              ^
2020-01-20T10:34:14.9793157Z HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
2020-01-20T10:34:14.9793379Z
2020-01-20T10:34:14.9793559Z
2020-01-20T10:34:14.9793800Z The above exception was the direct cause of the following exception:
2020-01-20T10:34:14.9793992Z
2020-01-20T10:34:14.9794231Z Traceback (most recent call last):
2020-01-20T10:34:14.9794979Z   File "/tmp/tmpvahl86j6/galaxy-dev/lib/galaxy/tools/__init__.py", line 1517, in handle_single_execution
2020-01-20T10:34:14.9803112Z     collection_info=collection_info,
2020-01-20T10:34:14.9804371Z   File "/tmp/tmpvahl86j6/galaxy-dev/lib/galaxy/tools/__init__.py", line 1599, in execute
2020-01-20T10:34:14.9845469Z     return self.tool_action.execute(self, trans, incoming=incoming, set_output_hid=set_output_hid, history=history, **kwargs)
2020-01-20T10:34:14.9846453Z   File "/tmp/tmpvahl86j6/galaxy-dev/lib/galaxy/tools/actions/__init__.py", line 517, in execute
2020-01-20T10:34:14.9846652Z     handle_output(name, output)
2020-01-20T10:34:14.9847218Z   File "/tmp/tmpvahl86j6/galaxy-dev/lib/galaxy/tools/actions/__init__.py", line 374, in handle_output
2020-01-20T10:34:14.9901226Z     data = trans.sa_session.query(app.model.HistoryDatasetAssociation).get(dataid)
2020-01-20T10:34:14.9902192Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 982, in get
2020-01-20T10:34:14.9902563Z     return self._get_impl(ident, loading.load_on_pk_identity)
2020-01-20T10:34:14.9903210Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 1094, in _get_impl
2020-01-20T10:34:14.9903870Z     return db_load_fn(self, primary_key_identity)
2020-01-20T10:34:14.9904659Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/loading.py", line 284, in load_on_pk_identity
2020-01-20T10:34:14.9905130Z     return q.one()
2020-01-20T10:34:14.9905712Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3325, in one
2020-01-20T10:34:14.9906010Z     ret = self.one_or_none()
2020-01-20T10:34:14.9906608Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3294, in one_or_none
2020-01-20T10:34:14.9906934Z     ret = list(self)
2020-01-20T10:34:14.9907533Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3367, in __iter__
2020-01-20T10:34:14.9908680Z     return self._execute_and_instances(context)
2020-01-20T10:34:14.9909655Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3392, in _execute_and_instances
2020-01-20T10:34:14.9910213Z     result = conn.execute(querycontext.statement, self._params)
2020-01-20T10:34:14.9910856Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 982, in execute
2020-01-20T10:34:14.9911149Z     return meth(self, multiparams, params)
2020-01-20T10:34:14.9911929Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/sql/elements.py", line 287, in _execute_on_connection
2020-01-20T10:34:14.9912190Z     return connection._execute_clauseelement(self, multiparams, params)
2020-01-20T10:34:14.9913022Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1101, in _execute_clauseelement
2020-01-20T10:34:14.9913329Z     distilled_params,
2020-01-20T10:34:14.9914298Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1250, in _execute_context
2020-01-20T10:34:14.9914575Z     e, statement, parameters, cursor, context
2020-01-20T10:34:14.9915355Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1476, in _handle_dbapi_exception
2020-01-20T10:34:14.9915624Z     util.raise_from_cause(sqlalchemy_exception, exc_info)
2020-01-20T10:34:14.9916183Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 398, in raise_from_cause
2020-01-20T10:34:14.9916453Z     reraise(type(exception), exception, tb=exc_tb, cause=cause)
2020-01-20T10:34:14.9917093Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 152, in reraise
2020-01-20T10:34:14.9918037Z     raise value.with_traceback(tb)
2020-01-20T10:34:14.9918720Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1246, in _execute_context
2020-01-20T10:34:14.9919011Z     cursor, statement, parameters, context
2020-01-20T10:34:14.9919626Z   File "/home/runner/.planemo/gx_venv_3.7_release_20.01/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 581, in do_execute
2020-01-20T10:34:14.9919938Z     cursor.execute(statement, parameters)
2020-01-20T10:34:14.9920164Z sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedFunction) operator does not exist: integer = boolean
2020-01-20T10:34:14.9920430Z LINE 3: WHERE history_dataset_association.id = true
2020-01-20T10:34:14.9920667Z                                              ^
2020-01-20T10:34:14.9920906Z HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
2020-01-20T10:34:14.9921594Z
2020-01-20T10:34:14.9923082Z [SQL: SELECT history_dataset_association.id AS history_dataset_association_id, history_dataset_association.history_id AS history_dataset_association_history_id, history_dataset_association.dataset_id AS history_dataset_association_dataset_id, history_dataset_association.create_time AS history_dataset_association_create_time, history_dataset_association.update_time AS history_dataset_association_update_time, history_dataset_association.state AS history_dataset_association_state, history_dataset_association.copied_from_history_dataset_association_id AS history_dataset_association_copied_from_history_dataset_a_1, history_dataset_association.copied_from_library_dataset_dataset_association_id AS history_dataset_association_copied_from_library_dataset_d_2, history_dataset_association.name AS history_dataset_association_name, history_dataset_association.info AS history_dataset_association_info, history_dataset_association.blurb AS history_dataset_association_blurb, history_dataset_association.peek AS history_dataset_association_peek, history_dataset_association.tool_version AS history_dataset_association_tool_version, history_dataset_association.extension AS history_dataset_association_extension, history_dataset_association.parent_id AS history_dataset_association_parent_id, history_dataset_association.designation AS history_dataset_association_designation, history_dataset_association.deleted AS history_dataset_association_deleted, history_dataset_association.visible AS history_dataset_association_visible, history_dataset_association.extended_metadata_id AS history_dataset_association_extended_metadata_id, history_dataset_association.version AS history_dataset_association_version, history_dataset_association.hid AS history_dataset_association_hid, history_dataset_association.purged AS history_dataset_association_purged, history_dataset_association.validated_state AS history_dataset_association_validated_state, history_dataset_association.validated_state_message AS history_dataset_association_validated_state_message, history_dataset_association.hidden_beneath_collection_instance_id AS history_dataset_association_hidden_beneath_collection_ins_3, dataset_1.id AS dataset_1_id, dataset_1.create_time AS dataset_1_create_time, dataset_1.update_time AS dataset_1_update_time, dataset_1.state AS dataset_1_state, dataset_1.deleted AS dataset_1_deleted, dataset_1.purged AS dataset_1_purged, dataset_1.purgable AS dataset_1_purgable, dataset_1.object_store_id AS dataset_1_object_store_id, dataset_1.external_filename AS dataset_1_external_filename, dataset_1._extra_files_path AS dataset_1__extra_files_path, dataset_1.created_from_basename AS dataset_1_created_from_basename, dataset_1.file_size AS dataset_1_file_size, dataset_1.total_size AS dataset_1_total_size, dataset_1.uuid AS dataset_1_uuid
2020-01-20T10:34:14.9924421Z FROM history_dataset_association LEFT OUTER JOIN dataset AS dataset_1 ON dataset_1.id = history_dataset_association.dataset_id
2020-01-20T10:34:14.9925146Z WHERE history_dataset_association.id = %(param_1)s]
2020-01-20T10:34:14.9925764Z [parameters: {'param_1': True}]
2020-01-20T10:34:14.9926632Z (Background on this error at: http://sqlalche.me/e/f405)
2020-01-20T10:34:14.9927166Z 2020-01-20 10:34:14,978 WARNI [galaxy.tools.execute] There was a failure executing a job for tool [disco] - Error executing tool: (psycopg2.errors.UndefinedFunction) operator does not exist: integer = boolean
2020-01-20T10:34:14.9927664Z LINE 3: WHERE history_dataset_association.id = true
2020-01-20T10:34:14.9927807Z                                              ^
2020-01-20T10:34:14.9928361Z HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
2020-01-20T10:34:14.9928441Z
2020-01-20T10:34:14.9929588Z [SQL: SELECT history_dataset_association.id AS history_dataset_association_id, history_dataset_association.history_id AS history_dataset_association_history_id, history_dataset_association.dataset_id AS history_dataset_association_dataset_id, history_dataset_association.create_time AS history_dataset_association_create_time, history_dataset_association.update_time AS history_dataset_association_update_time, history_dataset_association.state AS history_dataset_association_state, history_dataset_association.copied_from_history_dataset_association_id AS history_dataset_association_copied_from_history_dataset_a_1, history_dataset_association.copied_from_library_dataset_dataset_association_id AS history_dataset_association_copied_from_library_dataset_d_2, history_dataset_association.name AS history_dataset_association_name, history_dataset_association.info AS history_dataset_association_info, history_dataset_association.blurb AS history_dataset_association_blurb, history_dataset_association.peek AS history_dataset_association_peek, history_dataset_association.tool_version AS history_dataset_association_tool_version, history_dataset_association.extension AS history_dataset_association_extension, history_dataset_association.parent_id AS history_dataset_association_parent_id, history_dataset_association.designation AS history_dataset_association_designation, history_dataset_association.deleted AS history_dataset_association_deleted, history_dataset_association.visible AS history_dataset_association_visible, history_dataset_association.extended_metadata_id AS history_dataset_association_extended_metadata_id, history_dataset_association.version AS history_dataset_association_version, history_dataset_association.hid AS history_dataset_association_hid, history_dataset_association.purged AS history_dataset_association_purged, history_dataset_association.validated_state AS history_dataset_association_validated_state, history_dataset_association.validated_state_message AS history_dataset_association_validated_state_message, history_dataset_association.hidden_beneath_collection_instance_id AS history_dataset_association_hidden_beneath_collection_ins_3, dataset_1.id AS dataset_1_id, dataset_1.create_time AS dataset_1_create_time, dataset_1.update_time AS dataset_1_update_time, dataset_1.state AS dataset_1_state, dataset_1.deleted AS dataset_1_deleted, dataset_1.purged AS dataset_1_purged, dataset_1.purgable AS dataset_1_purgable, dataset_1.object_store_id AS dataset_1_object_store_id, dataset_1.external_filename AS dataset_1_external_filename, dataset_1._extra_files_path AS dataset_1__extra_files_path, dataset_1.created_from_basename AS dataset_1_created_from_basename, dataset_1.file_size AS dataset_1_file_size, dataset_1.total_size AS dataset_1_total_size, dataset_1.uuid AS dataset_1_uuid
2020-01-20T10:34:14.9930717Z FROM history_dataset_association LEFT OUTER JOIN dataset AS dataset_1 ON dataset_1.id = history_dataset_association.dataset_id
2020-01-20T10:34:14.9930866Z WHERE history_dataset_association.id = %(param_1)s]
2020-01-20T10:34:14.9931266Z [parameters: {'param_1': True}]
2020-01-20T10:34:14.9931395Z (Background on this error at: http://sqlalche.me/e/f405)
2020-01-20T10:34:14.9931685Z ERROR

# Fixed code
def execute(
    self,
    tool,
    trans,
    incoming=None,
    return_job=False,
    set_output_hid=True,
    history=None,
    job_params=None,
    rerun_remap_job_id=None,
    execution_cache=None,
    dataset_collection_elements=None,
    completed_job=None,
    collection_info=None,
):
    """
    Executes a tool, creating job and tool outputs, associating them, and
    submitting the job to the job queue. If history is not specified, use
    trans.history as destination for tool's output datasets.
    """
    trans.check_user_activation()
    incoming = incoming or {}
    self._check_access(tool, trans)
    app = trans.app
    if execution_cache is None:
        execution_cache = ToolExecutionCache(trans)
    current_user_roles = execution_cache.current_user_roles
    history, inp_data, inp_dataset_collections, preserved_tags, all_permissions = (
        self._collect_inputs(
            tool, trans, incoming, history, current_user_roles, collection_info
        )
    )
    # Build name for output datasets based on tool name and input names
    on_text = self._get_on_text(inp_data)

    # format='input" previously would give you a random extension from
    # the input extensions, now it should just give "input" as the output
    # format.
    input_ext = "data" if tool.profile < 16.04 else "input"
    input_dbkey = incoming.get("dbkey", "?")
    for name, data in reversed(list(inp_data.items())):
        if not data:
            data = NoneDataset(datatypes_registry=app.datatypes_registry)
            continue

        # Convert LDDA to an HDA.
        if isinstance(data, LibraryDatasetDatasetAssociation) and not completed_job:
            data = data.to_history_dataset_association(None)
            inp_data[name] = data

        if tool.profile < 16.04:
            input_ext = data.ext

        if data.dbkey not in [None, "?"]:
            input_dbkey = data.dbkey

        identifier = getattr(data, "element_identifier", None)
        if identifier is not None:
            incoming["%s|__identifier__" % name] = identifier

    # Collect chromInfo dataset and add as parameters to incoming
    (chrom_info, db_dataset) = execution_cache.get_chrom_info(tool.id, input_dbkey)

    if db_dataset:
        inp_data.update({"chromInfo": db_dataset})
    incoming["chromInfo"] = chrom_info

    if not completed_job:
        # Determine output dataset permission/roles list
        existing_datasets = [inp for inp in inp_data.values() if inp]
        if existing_datasets:
            output_permissions = app.security_agent.guess_derived_permissions(
                all_permissions
            )
        else:
            # No valid inputs, we will use history defaults
            output_permissions = app.security_agent.history_get_default_permissions(
                history
            )

    # Add the dbkey to the incoming parameters
    incoming["dbkey"] = input_dbkey
    # wrapped params are used by change_format action and by output.label; only perform this wrapping once, as needed
    wrapped_params = self._wrapped_params(trans, tool, incoming, inp_data)

    out_data = OrderedDict()
    input_collections = dict((k, v[0][0]) for k, v in inp_dataset_collections.items())
    output_collections = OutputCollections(
        trans,
        history,
        tool=tool,
        tool_action=self,
        input_collections=input_collections,
        dataset_collection_elements=dataset_collection_elements,
        on_text=on_text,
        incoming=incoming,
        params=wrapped_params.params,
        job_params=job_params,
        tags=preserved_tags,
    )

    # Keep track of parent / child relationships, we'll create all the
    # datasets first, then create the associations
    parent_to_child_pairs = []
    child_dataset_names = set()
    object_store_populator = ObjectStorePopulator(app)
    async_tool = tool.tool_type == "data_source_async"

    def handle_output(name, output, hidden=None):
        if output.parent:
            parent_to_child_pairs.append((output.parent, name))
            child_dataset_names.add(name)
        if async_tool and name in incoming:
            # HACK: output data has already been created as a result of the async controller
            dataid = incoming[name]
            data = trans.sa_session.query(app.model.HistoryDatasetAssociation).get(
                dataid
            )
            assert data is not None
            out_data[name] = data
        else:
            ext = determine_output_format(
                output,
                wrapped_params.params,
                inp_data,
                inp_dataset_collections,
                input_ext,
                python_template_version=tool.python_template_version,
            )
            create_datasets = True
            dataset = None

            if completed_job:
                for output_dataset in completed_job.output_datasets:
                    if output_dataset.name == name:
                        create_datasets = False
                        completed_data = output_dataset.dataset
                        dataset = output_dataset.dataset.dataset
                        break

            data = app.model.HistoryDatasetAssociation(
                extension=ext,
                dataset=dataset,
                create_dataset=create_datasets,
                flush=False,
            )
            if create_datasets:
                from_work_dir = output.from_work_dir
                if from_work_dir is not None:
                    data.dataset.created_from_basename = os.path.basename(from_work_dir)
            if hidden is None:
                hidden = output.hidden
            if (
                not hidden and dataset_collection_elements is not None
            ):  # Mapping over a collection - hide datasets
                hidden = True
            if hidden:
                data.visible = False
            if (
                dataset_collection_elements is not None
                and name in dataset_collection_elements
            ):
                dataset_collection_elements[name].hda = data
            trans.sa_session.add(data)
            if not completed_job:
                trans.app.security_agent.set_all_dataset_permissions(
                    data.dataset, output_permissions, new=True
                )
        data.copy_tags_to(preserved_tags)

        if (
            not completed_job
            and trans.app.config.legacy_eager_objectstore_initialization
        ):
            # Must flush before setting object store id currently.
            trans.sa_session.flush()
            object_store_populator.set_object_store_id(data)

        # This may not be neccesary with the new parent/child associations
        data.designation = name
        # Copy metadata from one of the inputs if requested.

        # metadata source can be either a string referencing an input
        # or an actual object to copy.
        metadata_source = output.metadata_source
        if metadata_source:
            if isinstance(metadata_source, string_types):
                metadata_source = inp_data.get(metadata_source)

        if metadata_source is not None:
            data.init_meta(copy_from=metadata_source)
        else:
            data.init_meta()
        # Take dbkey from LAST input
        data.dbkey = str(input_dbkey)
        # Set state
        if completed_job:
            data.blurb = completed_data.blurb
            data.peek = completed_data.peek
            data._metadata = completed_data._metadata
        else:
            data.blurb = "queued"
        # Set output label
        data.name = self.get_output_name(
            output,
            data,
            tool,
            on_text,
            trans,
            incoming,
            history,
            wrapped_params.params,
            job_params,
        )
        # Store output
        out_data[name] = data
        if output.actions:
            # Apply pre-job tool-output-dataset actions; e.g. setting metadata, changing format
            output_action_params = dict(out_data)
            output_action_params.update(incoming)
            output.actions.apply_action(data, output_action_params)
        # Also set the default values of actions of type metadata
        self.set_metadata_defaults(
            output,
            data,
            tool,
            on_text,
            trans,
            incoming,
            history,
            wrapped_params.params,
            job_params,
        )
        # Flush all datasets at once.
        return data

    for name, output in tool.outputs.items():
        if not filter_output(output, incoming):
            handle_output_timer = ExecutionTimer()
            if output.collection:
                collections_manager = app.dataset_collections_service
                element_identifiers = []
                known_outputs = output.known_outputs(
                    input_collections, collections_manager.type_registry
                )
                created_element_datasets = []
                # Just to echo TODO elsewhere - this should be restructured to allow
                # nested collections.
                for output_part_def in known_outputs:
                    # Add elements to top-level collection, unless nested...
                    current_element_identifiers = element_identifiers
                    current_collection_type = output.structure.collection_type

                    for parent_id in output_part_def.parent_ids or []:
                        # TODO: replace following line with formal abstractions for doing this.
                        current_collection_type = ":".join(
                            current_collection_type.split(":")[1:]
                        )
                        name_to_index = dict(
                            (value["name"], index)
                            for (index, value) in enumerate(current_element_identifiers)
                        )
                        if parent_id not in name_to_index:
                            if parent_id not in current_element_identifiers:
                                index = len(current_element_identifiers)
                                current_element_identifiers.append(
                                    dict(
                                        name=parent_id,
                                        collection_type=current_collection_type,
                                        src="new_collection",
                                        element_identifiers=[],
                                    )
                                )
                            else:
                                index = name_to_index[parent_id]
                        current_element_identifiers = current_element_identifiers[
                            index
                        ]["element_identifiers"]

                    effective_output_name = output_part_def.effective_output_name
                    element = handle_output(
                        effective_output_name, output_part_def.output_def, hidden=True
                    )
                    created_element_datasets.append(element)
                    # TODO: this shouldn't exist in the top-level of the history at all
                    # but for now we are still working around that by hiding the contents
                    # there.
                    # Following hack causes dataset to no be added to history...
                    child_dataset_names.add(effective_output_name)
                    trans.sa_session.add(element)
                    current_element_identifiers.append(
                        {
                            "__object__": element,
                            "name": output_part_def.element_identifier,
                        }
                    )

                history.add_datasets(
                    trans.sa_session,
                    created_element_datasets,
                    set_hid=set_output_hid,
                    quota=False,
                    flush=True,
                )
                if output.dynamic_structure:
                    assert not element_identifiers  # known_outputs must have been empty
                    element_kwds = dict(
                        elements=collections_manager.ELEMENTS_UNINITIALIZED
                    )
                else:
                    element_kwds = dict(element_identifiers=element_identifiers)
                output_collections.create_collection(
                    output=output, name=name, **element_kwds
                )
                log.info(
                    "Handled collection output named %s for tool %s %s"
                    % (name, tool.id, handle_output_timer)
                )
            else:
                handle_output(name, output)
                log.info(
                    "Handled output named %s for tool %s %s"
                    % (name, tool.id, handle_output_timer)
                )

    add_datasets_timer = ExecutionTimer()
    # Add all the top-level (non-child) datasets to the history unless otherwise specified
    datasets_to_persist = []
    for name, data in out_data.items():
        if (
            name not in child_dataset_names and name not in incoming
        ):  # don't add children; or already existing datasets, i.e. async created
            datasets_to_persist.append(data)
    # Set HID and add to history.
    # This is brand new and certainly empty so don't worry about quota.
    history.add_datasets(
        trans.sa_session,
        datasets_to_persist,
        set_hid=set_output_hid,
        quota=False,
        flush=False,
    )

    # Add all the children to their parents
    for parent_name, child_name in parent_to_child_pairs:
        parent_dataset = out_data[parent_name]
        child_dataset = out_data[child_name]
        parent_dataset.children.append(child_dataset)

    log.info("Added output datasets to history %s" % add_datasets_timer)
    job_setup_timer = ExecutionTimer()
    # Create the job object
    job, galaxy_session = self._new_job_for_session(trans, tool, history)
    self._record_inputs(trans, tool, job, incoming, inp_data, inp_dataset_collections)
    self._record_outputs(job, out_data, output_collections)
    job.object_store_id = object_store_populator.object_store_id
    if job_params:
        job.params = dumps(job_params)
    if completed_job:
        job.set_copied_from_job_id(completed_job.id)
    trans.sa_session.add(job)
    # Now that we have a job id, we can remap any outputs if this is a rerun and the user chose to continue dependent jobs
    # This functionality requires tracking jobs in the database.
    if app.config.track_jobs_in_database and rerun_remap_job_id is not None:
        self._remap_job_on_rerun(
            trans=trans,
            galaxy_session=galaxy_session,
            rerun_remap_job_id=rerun_remap_job_id,
            current_job=job,
            out_data=out_data,
        )
    log.info(
        "Setup for job %s complete, ready to be enqueued %s"
        % (job.log_str(), job_setup_timer)
    )

    # Some tools are not really executable, but jobs are still created for them ( for record keeping ).
    # Examples include tools that redirect to other applications ( epigraph ).  These special tools must
    # include something that can be retrieved from the params ( e.g., REDIRECT_URL ) to keep the job
    # from being queued.
    if "REDIRECT_URL" in incoming:
        # Get the dataset - there should only be 1
        for name in inp_data.keys():
            dataset = inp_data[name]
        redirect_url = tool.parse_redirect_url(dataset, incoming)
        # GALAXY_URL should be include in the tool params to enable the external application
        # to send back to the current Galaxy instance
        GALAXY_URL = incoming.get("GALAXY_URL", None)
        assert GALAXY_URL is not None, "GALAXY_URL parameter missing in tool config."
        redirect_url += "&GALAXY_URL=%s" % GALAXY_URL
        # Job should not be queued, so set state to ok
        job.set_state(app.model.Job.states.OK)
        job.info = "Redirected to: %s" % redirect_url
        trans.sa_session.add(job)
        trans.sa_session.flush()
        trans.response.send_redirect(
            url_for(
                controller="tool_runner", action="redirect", redirect_url=redirect_url
            )
        )
    else:
        # Dispatch to a job handler. enqueue() is responsible for flushing the job
        app.job_manager.enqueue(job, tool=tool)
        trans.log_event(
            "Added job to the job queue, id: %s" % str(job.id), tool_id=job.tool_id
        )
        return job, out_data