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 """ 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)) def fail(_): self._close_transaction() hydrant = DataHydrator() 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_all() self._connection.fetch_message() except ConnectionExpired as error: raise SessionExpired(*error.args) return result
def run(self, query, parameters=None, **kwparameters): """ Run a Cypher query within an auto-commit transaction. The query is sent and the result header received immediately but the :class:`neo4j.Result` content is fetched lazily as consumed by the client application. If a query is executed before a previous :class:`neo4j.Result` 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 query. 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 query: Cypher query :param parameters: dictionary of parameters :param kwparameters: additional keyword parameters :returns: :class:`neo4j.Result` object """ if not query: raise ValueError("Cannot run an empty query") if not isinstance(query, (str, Query)): raise TypeError("query must be a string or a Query instance") if not self._connection: self._connect(self._config.default_access_mode, database=self._config.database) cx = self._connection protocol_version = cx.PROTOCOL_VERSION server_info = cx.server_info has_transaction = self.has_transaction() query_text = str(query) query_metadata = getattr(query, "metadata", None) query_timeout = getattr(query, "timeout", None) parameters = DataDehydrator.fix_parameters(dict(parameters or {}, **kwparameters)) def fail(_): self._close_transaction() hydrant = DataHydrator() result_metadata = { "query": query_text, "parameters": parameters, "server": server_info, "protocol_version": protocol_version, } run_metadata = { "metadata": query_metadata, "timeout": query_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 = Result(self, hydrant, result_metadata) access_mode = None db = None bookmarks = None if has_transaction: # Explicit Transaction Run does not carry any extra values. RUN "query" {parameters} {extra} if query_metadata: raise ValueError("Metadata can only be attached at transaction level") if query_timeout: raise ValueError("Timeouts only apply at transaction level") access_mode = None db = None bookmarks = None else: run_metadata["bookmarks"] = self._bookmarks_in access_mode = self._config.default_access_mode db = self._config.database bookmarks = run_metadata.get("bookmarks", self._config.bookmarks) # BOLT RUN cx.run( query_text, parameters=parameters, mode=access_mode, bookmarks=bookmarks, metadata=run_metadata["metadata"], timeout=run_metadata["timeout"], db=db, on_success=run_metadata["on_success"], on_failure=run_metadata["on_failure"], ) # BOLT PULL cx.pull( 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: self._connection.send_all() self._connection.fetch_message() return result