<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def run(self, statement, parameters=None, **kwparameters):
 """ Run a Cypher statement within an auto-commit transaction. The statement is sent and the result header received immediately but the :class:`.StatementResult` content is fetched lazily as consumed by the client application. If a statement is executed before a previous :class:`.StatementResult` in the same :class:`.Session` has been fully consumed, the first result will be fully fetched and buffered. Note therefore that the generally recommended pattern of usage is to fully consume one result before executing a subsequent statement. If two results need to be consumed in parallel, multiple :class:`.Session` objects can be used as an alternative to result buffering. For more usage details, see :meth:`.Transaction.run`. :param statement: template Cypher statement :param parameters: dictionary of parameters :param kwparameters: additional keyword parameters :returns: :class:`.StatementResult` object """

        from neobolt.exceptions import ConnectionExpired

        self._assert_open()
        if not statement:
            raise ValueError("Cannot run an empty statement")
        if not isinstance(statement, (str, Statement)):
            raise TypeError("Statement must be a string or a Statement instance")

        if not self._connection:
            self._connect()
        cx = self._connection
        protocol_version = cx.protocol_version
        server = cx.server

        has_transaction = self.has_transaction()

        statement_text = str(statement)
        statement_metadata = getattr(statement, "metadata", None)
        statement_timeout = getattr(statement, "timeout", None)
        parameters = fix_parameters(dict(parameters or {}, **kwparameters), protocol_version,
                                    supports_bytes=server.supports("bytes"))

        def fail(_):
            self._close_transaction()

        hydrant = PackStreamHydrator(protocol_version)
        result_metadata = {
            "statement": statement_text,
            "parameters": parameters,
            "server": server,
            "protocol_version": protocol_version,
        }
        run_metadata = {
            "metadata": statement_metadata,
            "timeout": statement_timeout,
            "on_success": result_metadata.update,
            "on_failure": fail,
        }

        def done(summary_metadata):
            result_metadata.update(summary_metadata)
            bookmark = result_metadata.get("bookmark")
            if bookmark:
                self._bookmarks_in = tuple([bookmark])
                self._bookmark_out = bookmark

        self._last_result = result = BoltStatementResult(self, hydrant, result_metadata)

        if has_transaction:
            if statement_metadata:
                raise ValueError("Metadata can only be attached at transaction level")
            if statement_timeout:
                raise ValueError("Timeouts only apply at transaction level")
        else:
            run_metadata["bookmarks"] = self._bookmarks_in

        cx.run(statement_text, parameters, **run_metadata)
        cx.pull_all(
            on_records=lambda records: result._records.extend(
                hydrant.hydrate_records(result.keys(), records)),
            on_success=done,
            on_failure=fail,
            on_summary=lambda: result.detach(sync=False),
        )

        if not has_transaction:
            try:
                self._connection.send()
                self._connection.fetch()
            except ConnectionExpired as error:
                raise SessionExpired(*error.args)

        return result