def _run(self, query, parameters, db, access_mode, bookmarks, **kwparameters): query_text = str(query) # Query or string object query_metadata = getattr(query, "metadata", None) query_timeout = getattr(query, "timeout", None) parameters = DataDehydrator.fix_parameters( dict(parameters or {}, **kwparameters)) self._metadata = { "query": query_text, "parameters": parameters, "server": self._connection.server_info, } run_metadata = { "metadata": query_metadata, "timeout": query_timeout, } def on_attached(metadata): self._metadata.update(metadata) self._qid = metadata.get( "qid", -1 ) # For auto-commit there is no qid and Bolt 3 do not support qid self._keys = metadata.get("fields") self._attached = True def on_failed_attach(metadata): self._metadata.update(metadata) self._attached = False self._on_closed() self._connection.run( query_text, parameters=parameters, mode=access_mode, bookmarks=bookmarks, metadata=query_metadata, timeout=query_timeout, db=db, on_success=on_attached, on_failure=on_failed_attach, ) self._pull() self._connection.send_all() self._attach()
def dehydrated_value(value): return DataDehydrator.fix_parameters({"_": value})["_"]
def run(self, cypher, 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 cypher: Cypher statement :param parameters: dictionary of parameters :param kwparameters: additional keyword parameters :returns: :class:`.StatementResult` object """ if not cypher: raise ValueError("Cannot run an empty statement") if not isinstance(cypher, (str, Statement)): raise TypeError( "Statement must be a string or a Statement instance") if not self._connection: self._connect(self._config.default_access_mode) cx = self._connection protocol_version = cx.PROTOCOL_VERSION server = cx.server has_transaction = self.has_transaction() statement_text = str(cypher) statement_metadata = getattr(cypher, "metadata", None) statement_timeout = getattr(cypher, "timeout", None) parameters = DataDehydrator.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") # TODO: fail if explicit database name has been set else: run_metadata["bookmarks"] = self._bookmarks_in # TODO: capture ValueError and surface as SessionError/TransactionError if # TODO: explicit database selection has been made cx.run(statement_text, parameters, **run_metadata) 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
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