Skip to content

Introduce Driver.execute_query #531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b271a28
Introduce `Driver.execute_query`
bigmontz Oct 12, 2022
a932e10
add test_execute_query_without_config
bigmontz Oct 12, 2022
1985a82
Add tests for test_configure_routing_to_readers and test_configure_ro…
bigmontz Oct 12, 2022
c6c4b16
Add tests to test_configure_database
bigmontz Oct 12, 2022
6ad790f
Add test_configure_impersonated_user
bigmontz Oct 12, 2022
02d8b64
Address comments in the PR
bigmontz Oct 12, 2022
ea76103
Apply suggestions from code review
bigmontz Oct 12, 2022
bb986e0
Add Feature flag
bigmontz Oct 12, 2022
6abe39d
Add test_causal_consistency_between_query_executions
bigmontz Oct 12, 2022
c6ef97a
Add test_disable_bookmark_manager
bigmontz Oct 12, 2022
b658d19
Rename property to bookmarkManagerId
bigmontz Oct 12, 2022
db4f804
Add test_configure_custom_bookmark_manager
bigmontz Oct 12, 2022
4a682e3
Enumerate new tests
bigmontz Oct 12, 2022
02ee8ed
add test_retry_on_retriable_error
bigmontz Oct 12, 2022
fa614c5
Add scripts
bigmontz Oct 12, 2022
7eaba13
Add test for test_thrown_non_retriable_error
bigmontz Oct 12, 2022
e61527e
Address comment in the PR
bigmontz Oct 13, 2022
f7bd0bb
Extracting config params and define bookmarkManagerId=-1 as the disab…
bigmontz Oct 14, 2022
7ab4027
Make query concistent
bigmontz Oct 14, 2022
e7fcdb9
Docs and summary assertion
bigmontz Oct 14, 2022
5f69bb1
Adjust expected and actual value order in assertions
bigmontz Oct 17, 2022
0267330
Stub script formatting
robsdedude Oct 17, 2022
d1a5e61
Some Python formatting
robsdedude Oct 17, 2022
3fcfc89
Apply suggestions from code review
bigmontz Oct 17, 2022
c5ca626
Address comments in the PR
bigmontz Oct 17, 2022
4740571
Improve `transaction_chaining.script`
bigmontz Oct 19, 2022
23d4279
Update tests/stub/driver_execute_query/scripts/transaction_chaining_c…
bigmontz Oct 19, 2022
16e2d99
Grammar
robsdedude Oct 19, 2022
e031aeb
Refactoring
robsdedude Oct 19, 2022
c9505ce
Fix messed up merge
robsdedude Oct 19, 2022
66771bc
Not filtering params from the ExecuteQuery
bigmontz Oct 19, 2022
5d9e945
Add missing optional RESET to script
robsdedude Oct 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions nutkit/frontend/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ def send_and_receive(self, req, timeout=None, hooks=None, *,
return self.receive(timeout=timeout, hooks=hooks,
allow_resolution=allow_resolution)

def execute_query(self, cypher, params=None,
routing=None, database=None,
impersonated_user=None, bookmark_manager=...):
config = {
"routing": routing,
"database": database,
"impersonatedUser": impersonated_user
}

if bookmark_manager is None:
config["bookmarkManagerId"] = -1
elif bookmark_manager is not Ellipsis:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's Ellipsis? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ...

config["bookmarkManagerId"] = bookmark_manager.id

req = protocol.ExecuteQuery(self._driver.id, cypher, params, config)
res = self.send_and_receive(req, allow_resolution=True)
if not isinstance(res, protocol.EagerResult):
raise Exception("Should be EagerResult but was: %s" % res)
return res

def verify_connectivity(self):
req = protocol.VerifyConnectivity(self._driver.id)
res = self.send_and_receive(req, allow_resolution=True)
Expand Down
3 changes: 3 additions & 0 deletions nutkit/protocol/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class Feature(Enum):
# is picked up instead or we need to wait until the full pool depletes.
API_CONNECTION_ACQUISITION_TIMEOUT = \
"Feature:API:ConnectionAcquisitionTimeout"
# The driver offers a method to run a query in a retryable context at the
# driver object level.
API_DRIVER_EXECUTE_QUERY = "Feature:API:Driver.ExecuteQuery"
# The driver offers a method for checking if a connection to the remote
# server of cluster can be established and retrieve the server info of the
# reached remote.
Expand Down
27 changes: 27 additions & 0 deletions nutkit/protocol/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,30 @@ def __init__(self, result_id, record_key, type_name, field_id):
self.recordKey = record_key
self.type = type_name
self.field = field_id


class ExecuteQuery:
"""
Request to execute a query in a retriable context.

Backend should return EagerResult or a Error response.

:param driver_id: The id of the driver where the cypher query has to run.
:param cypher: The cypher query which to run.
:param params: The cypher query params.
:param config: The configuration
:param config.database: The database where the query will run.
:param config.routing: The type of routing ("w" for Writers,
"r" for "Readers")
:param config.impersonatedUser: The user which will be impersonated
:param config.bookmarkManagerId: The id of the bookmark manager
used in the query. None or not define for using the default,
-1 for disabling the BookmarkManager
"""

def __init__(self, driver_id, cypher, params, config):
self.driverId = driver_id
self.cypher = cypher
self.params = params
if config:
self.config = config
7 changes: 7 additions & 0 deletions nutkit/protocol/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@ def __init__(self, inUse, idle):
self.idle = idle


class EagerResult:
def __init__(self, keys, records, summary):
self.keys = keys
self.records = [Record(**record) for record in records or []]
self.summary = Summary(**summary)


class BaseError(Exception):
"""
Base class for all types of errors, should not be sent from backend.
Expand Down
Empty file.
10 changes: 10 additions & 0 deletions tests/stub/driver_execute_query/scripts/router.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!: BOLT 5.0
!: ALLOW RESTART

A: HELLO {"{}": "*"}
{+
C: ROUTE "*" "*" "*"
S: SUCCESS {"rt": {"ttl": 1000, "db": "adb", "servers": [{"addresses": ["#HOST#:9000"], "role":"ROUTE"}, {"addresses": ["#HOST#:9010"], "role":"READ"}, {"addresses": ["#HOST#:9020"], "role":"WRITE"}]}}
?: RESET
+}
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
!: BOLT 5.0
!: ALLOW RESTART

A: HELLO {"{}": "*"}
C: ROUTE "*" "*" "*"
S: SUCCESS {"rt": {"ttl": 1000, "db": "adb", "servers": [{"addresses": ["#HOST#:9000"], "role":"ROUTE"}, {"addresses": ["#HOST#:9010"], "role":"READ"}, {"addresses": ["#HOST#:9020"], "role":"WRITE"}]}}
?: RESET
C: ROUTE "*" "*" "*"
S: SUCCESS {"rt": {"ttl": 1000, "db": "adb", "servers": [{"addresses": ["#HOST#:9000"], "role":"ROUTE"}, {"addresses": ["#HOST#:9020"], "role":"READ"}, {"addresses": ["#HOST#:9010"], "role":"WRITE"}]}}
?: RESET

?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
!: BOLT 5.0
!: ALLOW RESTART

A: HELLO {"{}": "*"}
C: ROUTE "*" "*" {"db":"neo4j"}
S: SUCCESS {"rt": {"ttl": 1000, "db": "neo4j", "servers": [{"addresses": ["#HOST#:9000"], "role":"ROUTE"}, {"addresses": ["#HOST#:9010"], "role":"READ"}, {"addresses": ["#HOST#:9020"], "role":"WRITE"}]}}
*: RESET
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
!: BOLT 5.0
!: ALLOW RESTART

A: HELLO {"{}": "*"}
C: ROUTE "*" "*" {"imp_user": "that-other-dude"}
S: SUCCESS {"rt": {"ttl": 1000, "db": "adb", "servers": [{"addresses": ["#HOST#:9000"], "role":"ROUTE"}, {"addresses": ["#HOST#:9010"], "role":"READ"}, {"addresses": ["#HOST#:9020"], "role":"WRITE"}]}}
*: RESET
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET

C: BEGIN {"[db]": "*"}
S: SUCCESS {}
C: RUN "CREATE (p:Person {name:$name}) RETURN p.name AS name" {"name": "the person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: RECORD ["the person"]
SUCCESS {"type": "w"}
C: COMMIT
S: SUCCESS {"bookmark": "bm1"}
*: RESET

{{
C: BEGIN {"[db]": "*"}
S: SUCCESS {}
C: RUN "MATCH (p:Person {name:$name}) RETURN p.name AS name" {"name": "the person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "bm1"}
----
C: BEGIN {"bookmarks": ["bm1"], "[db]": "*"}
S: SUCCESS {}
C: RUN "MATCH (p:Person {name:$name}) RETURN p.name AS name" {"name": "the person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: RECORD ["the person"]
SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "bm2"}
}}
*: RESET

?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET

C: BEGIN {"bookmarks": ["other_db:bm1"], "[db]": "*"}
S: SUCCESS {}
C: RUN "CREATE (p:Person {name:$name}) RETURN p.name AS name" {"name": "a person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: RECORD ["a person"]
SUCCESS {"type": "w"}
C: COMMIT
S: SUCCESS {"bookmark": "bm1"}
*: RESET

{{
C: BEGIN {"bookmarks": ["other_db:bm1"], "[db]": "*"}
S: SUCCESS {}
C: RUN "MATCH (p:Person {name:$name}) RETURN p.name AS name" {"name": "a person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "bm1"}
----
C: BEGIN {"bookmarks{}": ["bm1", "other_db:bm1"], "[db]": "*"}
S: SUCCESS {}
C: RUN "MATCH (p:Person {name:$name}) RETURN p.name AS name" {"name": "a person"} {}
S: SUCCESS {"fields": ["name"]}
C: PULL {"n": 1000}
S: RECORD ["a person"]
SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "bm2"}
}}
*: RESET

?: GOODBYE
15 changes: 15 additions & 0 deletions tests/stub/driver_execute_query/scripts/tx_return_1.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET
C: BEGIN {"{}": "*"}
S: SUCCESS {}
C: RUN "RETURN 1 AS n" {} {}
S: SUCCESS {"fields": ["n"], "qid": 1}
C: PULL {"n": 1000}
S: RECORD [1]
SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "neo4j:bookmark:v1:tx424242"}
*: RESET
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET
C: BEGIN {"{}": "*"}
S: SUCCESS {}
C: RUN "RETURN 1 AS n" {} {}
S: SUCCESS {"fields": ["n"], "qid": 1}
C: PULL {"n": 1000}
S: <EXIT>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET
C: BEGIN {"{}": "*"}
S: SUCCESS {}
C: RUN "RETURN 1 AS n" {} {}
S: SUCCESS {"fields": ["n"], "qid": 1}
C: PULL {"n": 1000}
S: FAILURE {"code": "Neo.ClientError.Transaction.Terminated", "message": "message"}
*: RESET
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET
C: BEGIN {"imp_user": "that-other-dude", "db": "adb"}
S: SUCCESS {}
C: RUN "RETURN 1 AS n" {} {}
S: SUCCESS {"fields": ["n"], "qid": 1}
C: PULL {"n": 1000}
S: RECORD [1]
SUCCESS {"type": "r"}
C: COMMIT
S: SUCCESS {"bookmark": "neo4j:bookmark:v1:tx424242"}
*: RESET
?: GOODBYE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
!: BOLT 5.0

A: HELLO {"{}": "*"}
*: RESET
C: BEGIN {"{}": "*"}
S: SUCCESS {}
C: RUN "RETURN $a AS n" { "a": 1 } {}
S: SUCCESS {"fields": ["n"], "qid": 1}
C: PULL {"n": 1000}
S: RECORD [1]
SUCCESS {"type": "r", "db": "#DB#"}
C: COMMIT
S: SUCCESS {"bookmark": "neo4j:bookmark:v1:tx424242"}
*: RESET
?: GOODBYE
Loading